FurryCat 07 января 2020

Чего ждать от JavaScript в 2020 году? 7 фич, которые вот-вот попадут в стандарт

Посмотрим, какие предложения по развитию языка добрались до четвертой стадии в процессе TC39. Примеряем новинки, сравниваем с текущим состоянием.
Чего ждать от JavaScript в 2020 году? 7 фич, которые вот-вот попадут в стандарт

Ура-ура! Сразу несколько приятных и полезных фич перешли на стадию завершения, а значит, в 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 с обычным регулярным выражением
Вызов метода String.prototype.match с обычным регулярным выражением

А для глобального регулярного выражения возвращается малоинформативный массив:

        str.match(reg);
    
Вызов метода String.prototype.match с глобальным регулярным выражением
Вызов метода String.prototype.match с глобальным регулярным выражением

В прототипе RegExp есть подходящий метод exec. Для обычного выражения он работает так же, как String.prototype.match – ничего интересного:

        re.exec(str);
    
Вызов метода RegExp.prototype.exeс для обычного регулярного выражения
Вызов метода RegExp.prototype.exeс для обычного регулярного выражения

С глобальными регулярками уже можно работать:

        reg.exec(str);
reg.exec(str);
reg.exec(str);
    
Вызов метода RegExp.prototype.exeс для глобального регулярного выражения
Вызов метода RegExp.prototype.exeс для глобального регулярного выражения

Каждый вызов возвращает следующее совпадение, а когда строка кончится – null. Чтобы перебрать длинные строки, можно воспользоваться циклом while. Позиция для начала поиска хранится в свойстве lastIndex объекта регулярного выражения. Это не так уж очевидно, правда?

На смену этому подходу пришел удобный и интуитивно понятный метод String.prototype.matchAll. Он возвращает итератор:

        str.matchAll(re);
    
Метод String.prototype.matchAll возвращает итератор
Метод String.prototype.matchAll возвращает итератор

Вызов next() выводит каждое следующее значение:

Перебор совпадений с помощью метода next
Перебор совпадений с помощью метода 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
    
  1. Значение BigInt не может быть строго равно значению Number, но выражение 2n == 2 истинно.
  2. При делении результат всегда будет округляться до нуля (4n / 10n = 0n).
  3. Нельзя смешивать в одной операции 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
Объект 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. Наслаждайтесь ;)

Какая из новых фич JavaScript вам нравится больше всего?

Источники

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Go-разработчик
по итогам собеседования
Разработчик С#
от 200000 RUB до 400000 RUB

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ