Ура-ура! Сразу несколько приятных и полезных фич перешли на стадию завершения, а значит, в 2020 году нас ждет много радостей. Наконец-то в стандарте появятся долгожданные опциональные последовательности и динамический импорт. Оставим их на сладкое, а сначала посмотрим, что еще приготовил разработчикам TC39.
Если вы не можете удержаться, загляните в репозиторий пропозалов на страничку Finished Proposals. Там вы найдете полный список предложений, достигших стадии 4.
String.prototype.matchAll
Представим, строка сопоставляется с подстрокой или регулярным выражением. Чтобы найти все совпадения, придется потрудиться. Ведь метод String.prototype.match для обычной регулярки возвращает информацию только о первом совпадении:
let re = /ll(o|y)/;
let reg = /ll(o|y)/g;
let str = 'Hello Dolly';
str.match(re);
![Вызов метода String.prototype.match с обычным регулярным выражением](https://media.proglib.io/posts/2020/01/06/41f6882f710830e6f780b402d2f747e1.png)
А для глобального регулярного выражения возвращается малоинформативный массив:
str.match(reg);
![Вызов метода String.prototype.match с глобальным регулярным выражением](https://media.proglib.io/posts/2020/01/06/9e9343b126c871c9b0f135b2cf1b8969.png)
В прототипе RegExp есть подходящий метод exec. Для обычного выражения он работает так же, как String.prototype.match
– ничего интересного:
re.exec(str);
![Вызов метода RegExp.prototype.exeс для обычного регулярного выражения](https://media.proglib.io/posts/2020/01/06/6af7785c2b44245ab8f9860b7c69ae6f.png)
С глобальными регулярками уже можно работать:
reg.exec(str);
reg.exec(str);
reg.exec(str);
![Вызов метода RegExp.prototype.exeс для глобального регулярного выражения](https://media.proglib.io/posts/2020/01/06/ba46de695ff517bf15565df5d8a77589.png)
Каждый вызов возвращает следующее совпадение, а когда строка кончится – null
. Чтобы перебрать длинные строки, можно воспользоваться циклом while
. Позиция для начала поиска хранится в свойстве lastIndex
объекта регулярного выражения. Это не так уж очевидно, правда?
На смену этому подходу пришел удобный и интуитивно понятный метод String.prototype.matchAll. Он возвращает итератор:
str.matchAll(re);
![Метод String.prototype.matchAll возвращает итератор](https://media.proglib.io/posts/2020/01/06/09829c6d403f279ed7b9917141987100.png)
Вызов next()
выводит каждое следующее значение:
![Перебор совпадений с помощью метода next](https://media.proglib.io/posts/2020/01/06/6f2c6d75fffc15e0b7595eeb9ef1e822.png)
Но можно воспользоваться уже имеющимися благами ES6 и сделать вот так:
[...str.matchAll(reg)];
![Деструктуризация итератора](https://media.proglib.io/posts/2020/01/06/858260b07313c7edddf8ffd3f58e3f4d.png)
Одной строчкой кода мы собрали полную информацию обо всех совпадениях в строке.
BigInt
До появления нового типа данных BigInt приходилось пользоваться типом Number. Самое большое число, которое можно было точно представить в JavaScript, равнялось 2⁵³-1 (константа Number.MAX_SAFE_INTEGER
).
Теперь не обязательно ограничивать себя этой ничтожной величиной. Расправьте крылья и воспарите в пространстве по-настоящему больших чисел.
Чтобы создать значение с типом BigInt, добавьте к числу n
или воспользуйтесь встроенной функцией BigInt()
.
let bigInt = 4n
console.log(bigInt * bigInt) // 16n
- Значение BigInt не может быть строго равно значению Number, но выражение
2n == 2
истинно. - При делении результат всегда будет округляться до нуля (
4n / 10n = 0n
). - Нельзя смешивать в одной операции BigInt и Number, так как неявное преобразование между этими типами не происходит (
4n + 2
).
globalThis
Получение доступа к глобальным объектам – это вечная головная боль в JavaScript. В каждой среде выполнения свои особенности, свой синтаксис, который нужно знать, чтобы ваш код можно было свободно портировать.
Обычно мы выходили из этого положения, создавая функцию getGlobal()
, внутри которой перебирали все возможные варианты:
const getGlobal = function() {
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('no global object found');
};
const globals = getGlobal();
После того как появился globalThis
, можно с чистой совестью забыть об этом зоопарке. Например, в браузере в объекте globalThis
вы найдете знакомый window
:
![Объект globalThis в браузере равен объекту window](https://media.proglib.io/posts/2020/01/06/a7bfb702724fb08fba04f259a79f3716.png)
Promise.allSettled
Если вам требуется дождаться выполнения сразу нескольких промисов, вы, конечно, обратитесь к методу Promise.all()
. Но он принимает только успешно выполненные «обещания». Если где-то появится ошибка, продолжить работу не получится.
Новому методу Promise.allSettled() наплевать, как завершились ваши промисы – главное, что они отработали.
const fulfilledPromise = Promise.resolve("success");
const rejectedPromise = Promise.reject("error")
const promises = [fulfilledPromise, rejectedPromise];
Promise.allSettled(promises)
.then((results) => results.forEach((result) => console.log(result)));
Динамический импорт
Еще одна новая фича JavaScript, которой вы наверняка будете пользоваться – динамический импорт. Теперь модули можно загружать прямо в рантайме – в зависимости от некоторого условия или просто отложенно, чтобы не загружать клиент. Достаточно вызвать функцию import()
, передать указатель на нужный модуль и получить ответный промис.
import("some_module")
.then(module => {
module.doSomething();
})
.catch(err => {
console.error(err.message);
});
Или даже пользоваться шаблонными строками, чтобы динамически указать имя модуля:
import(`${some_module}.js`)
.then(module => {
module.doSomething();
})
.catch(err => {
console.error(err.message);
});
Nullish coalescing
У вас есть некоторый объект. Нужно получить и вернуть значение одного из его свойств, если оно не равно null
или undefined
. В обратном случае возвратить значение по умолчанию. Как бы вы решили эту задачу?
Наверняка вы обратитесь к логическому оператору ИЛИ (||
):
const response = someResponse.properties.mayBeUndefined || 'Response';
Но есть небольшая проблема. Если в свойстве лежит пустая строка или ноль, мы все равно получим в ответе правую часть выражения ('Response'
), так как это ложные значения:
const someResponse = {properties: { mayBeUndefined: ''}}
const response = someResponse.properties.mayBeUndefined || 'Response';
console.log(response); // Response
Оператор Nullish coalescing, или оператор объединения с null
, позволяет решить эту задачу. Если свойство равно null
или undefined
, то вернется правое значение. Во всех остальных случаях – левое. Независимо от того, истинное оно или ложное.
const someResponse = {properties: { mayBeUndefined: ''}}
const response = someResponse.properties.mayBeUndefined ?? 'Response';
console.log(response); // '' – пустая строка
Опциональные последовательности
Если вы хоть раз пытались получить глубоко вложенное свойство объекта, то знаете, какая это мука. На каждом уровне поджидает ошибка – ведь искомого свойства может и не быть. Приходится действовать пошагово, доставать всю цепочку свойств по очереди, одно за другим. Это ужасно утомляет и нервирует.
const someObj = {
property: 'prop',
otherProperty: {
name: 'prop2'
}
};
const property = someObj.NotOtherProperty ? someObj.NotOtherProperty.name: undefined;
console.log(property); // undefined
Нас спасут опциональные последовательности (англ. Optional Chaining). Достаточно поставить рядом с потенциально несуществующим свойством оператор ?.
(вопросительный знак как символ сомнения и точка для стандартного доступа к свойству объекта).
const someObj = {
property: 'prop',
otherProperty: {
name: 'prop2'
}
};
const property = someObj.NotOtherProperty?.name;
console.log(property); // undefined
Результат тот же, а запись короче и понятнее!
Вы тоже не можете дождаться?
Если у вас чешутся руки использовать эти приятные возможности в работе, не ограничивайте себя! Многие из них уже воплощены в движках, и практически для всех есть плагины babel. Наслаждайтесь ;)
Комментарии