Чего ждать от JavaScript в 2020 году? 7 фич, которые вот-вот попадут в стандарт
Посмотрим, какие предложения по развитию языка добрались до четвертой стадии в процессе TC39. Примеряем новинки, сравниваем с текущим состоянием.
Ура-ура! Сразу несколько приятных и полезных фич перешли на стадию завершения, а значит, в 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);
А для глобального регулярного выражения возвращается малоинформативный массив:
str.match(reg);
В прототипе RegExp есть подходящий метод exec. Для обычного выражения он работает так же, как String.prototype.match
– ничего интересного:
re.exec(str);
С глобальными регулярками уже можно работать:
reg.exec(str); reg.exec(str); reg.exec(str);
Каждый вызов возвращает следующее совпадение, а когда строка кончится – null
. Чтобы перебрать длинные строки, можно воспользоваться циклом while
. Позиция для начала поиска хранится в свойстве lastIndex
объекта регулярного выражения. Это не так уж очевидно, правда?
На смену этому подходу пришел удобный и интуитивно понятный метод String.prototype.matchAll. Он возвращает итератор:
str.matchAll(re);
Вызов next()
выводит каждое следующее значение:
Но можно воспользоваться уже имеющимися благами ES6 и сделать вот так:
[...str.matchAll(reg)];
Одной строчкой кода мы собрали полную информацию обо всех совпадениях в строке.
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
:
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. Наслаждайтесь ;)