Недавно опциональный доступ к аттрибутам (Optional Chaining) и значение по умолчанию для атрибутов (Nullish Coalescing) перешли на последний, четвёртый этап процесса TC39.
На практике это означает, что эти и другие нововведения станут частью стандарта JavaScript уже в этом, 2020 году. Их мы и рассмотрим в этой статье.
Отслеживать поддержку браузерами можно здесь («2020 features») — прим. перев.
String.prototype.matchAll
Использование «липких» (sticky) или глобальных регулярных выражений, когда надо захватить несколько групп на одной строке, может оказаться нетривиальным.
String.prototype.match не возвращает захваченные группы при наличии глобального флага, а без него можно получить только первое полное соответствие шаблону и его группы.
Пример:
let regexp = /t(e)(st(\d?))/g;
let str = 'test1test2';
const results = str.match(regexp);
console.log(results);
Результат с флагом «g»
Результат без флага «g»
Использование String.prototype.matchAll гарантирует, что будут возвращены все соответствия и их группы.
Пример:
let regexp = /t(e)(st(\d?))/g;
let str = 'test1test2';
let array = [...str.matchAll(regexp)];
console.log(array);
Результат:
BigInt
До появления BigInt наибольшее значение, представляемое Number, было равно 2??-1 (MAX_SAFE_INTEGER). Теперь в JavaScript будет примитив, способный превзойти лимит.
Создать BigInt можно добавив 'n' к числу или используя функцию BigInt().
Пример:
let bigInt = 4n
console.log(bigInt * bigInt)
- BigInt не эквивалентен Number, но может быть приведён к последнему.
- При выполнении операций вроде деления, результат будет округлён до целого.
- Нельзя использовать с Number без приведения типов.
Попытка сложить BigInt с числом...
let bigInt = 4n + 2
console.log(bigInt)
… приводит к исключению:
globalThis
Доступ к глобальным объектам в мире JavaScript всегда был головной болью. Вы должны быть знакомы со специфическим синтаксисом для окружения, что создаёт лишние трудности при написании переносимого кода и приводит к необходимости использовать что-то вроде getGlobal.
Пример:
var 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');
};
var globals = getGlobal();
С появлением globalThis можно перестать думать об окружении и получать глобальные объекты унифицировано.
Пример:
globalThis.someFunction = () => 'hello'
console.log(globalThis.someFunction())
Promise.allSettled
Допустим, у вас есть несколько промисов, и вы хотите что-то сделать после их завершения (неважно, успешного или нет). 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)));
Результат:
Dynamic Import
Хотите загрузить модуль во время выполнения, в зависимости от определенного условия? Теперь это можно будет сделать без сторонних библиотек.
Достаточно вызвать функцию 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, мы обычно используем оператор ‘||’.
До Nullish coalescing:
const response = someResponse.properties.mayBeUndefined || 'Response';
Представьте, однако, что атрибут имеет «ложное» значение.
Проблема использования ‘||’:
const someResponse = {properties: { mayBeUndefined: ''}}
const response = someResponse.properties.mayBeUndefined || 'Response';
console.log(response)
Результат:
Это нежелательное поведение. В данном случае нам нужно было значение атрибута, а не значение по умолчанию.
С Nullish coalescing этой проблемы не будет. Значение по умолчанию будет возвращено только для атрибутов null или undefined.
Использование Nullish coalescing:
const someResponse = {properties: { mayBeUndefined: ''}}
const response = someResponse.properties.mayBeUndefined ?? 'Response';
console.log(response)
Результат:
Optional Chaining
Чтобы получить доступ к под-атрибуту, нам нужно убедиться, что атрибут выше существует. Пока что существование каждого из над-атрибутов нужно проверять вручную.
До Optional Chaining:
const someObj = {
property: 'prop',
otherProperty: {
name: 'prop2'
}
};
const property = someObj.NotOtherProperty ? someObj.NotOtherProperty.name: undefined;
console.log(property);
С появлением Optional Chaining, можно будет использовать оператор ‘?.’ для опционального доступа к под-атрибутам. Код ниже эквивалентен коду выше.
Использование Optional Chaining:
const someObj = {
property: 'prop',
otherProperty: {
name: 'prop2'
}
};
const property = someObj.NotOtherProperty?.name;
console.log(property);
Optional Chaining делает код гораздо чище, особенно, если атрибутов в строке много.
Заключение
Вы можете попробовать запустить примеры из статьи в консоли: некоторые из них уже реализованы в последних версиях браузеров, другие, вероятно, будут внедрены очень скоро. Так или иначе, к возможностям из статьи уже можно начинать относиться как к части языка образца 2020 года.
Комментарии (24)
vladimirx
09.01.2020 11:37+1Хоть мне и не лень написать код без Optional Chaining, но эта фича меня всегда радовала в других языках :) Радует как JS развивается в последние года.
Eldhenn
09.01.2020 13:41> Optional Chaining
Я джвадцать джва года ждал эту фичу!BerkutEagle
09.01.2020 14:52Я джвадцать джва года ждал эту
Uncaught TypeError: Cannot read property 'feature' of undefined
OlegSchwann
09.01.2020 20:29> Попытка сложить BigInt с числом
4n + 2
приводит к исключению.
Удивляет, что от первоначальной философии языка отошли.
Впрочем, это хорошо и удобно для всех, кроме участников www.dwitter.net.ReklatsMasters
11.01.2020 02:07Вообще это правильно что прекратили порочную практику сложения тёплого с мягким.
Nookie-Grey
12.01.2020 22:56да, чёт странно, можно скаладывать всё что угодно, кроме BigInt, это явно не javascript
Format-X22
10.01.2020 01:32А в NodeJS удобно использовать этот сайт — https://node.green/
Как только LTS версия ноды делает шаг вперёд — самое время проверять чего добавили и изучать если что было пропущено.
BigInt уже там давно и лично продакшн код с ним писал, а вот например штуки с опциональным чейнингом посвежее.
А ещё можно наблюдать как в язык завозят приватные свойства и, судя по всему, скоро завезут и приватные методы.
Вообще там есть чего почитать и будущее прикинуть, один из мной любимых сайтов по фичам и развитию языка уже многие годы.
ReklatsMasters
11.01.2020 02:09В 13 версию уже видел pr с приватными свойствами, выглядит классно, хоть я и был против такого синтаксиса.
sanchezzzhak
10.01.2020 10:26Сожалению Nullish Coalescing, Optional Chaining работают немного не так, как хочется.
Проверка идет только последнего элемента на (null, void 0), а не всей цепочки.ratijas
10.01.2020 13:47+1В каких жизненных ситуациях вы хотели бы, чтобы одним оператором делалось более одного действия сразу? О_о
Aingis
10.01.2020 19:22В смысле «не всей цепочки»? Что мешает использовать Optional chaining во всей цепочке?
someObj.?property.?mayBeUndefined ?? 'Response'
Nookie-Grey
12.01.2020 23:07Optional Chaining проверят тот элемент, к которому применён, а не последний.
In a deeply nested chain like a?.b?.c, why should I write?.. at each level? Should I not be able to write the operator only once for the whole chain?
By design, we want the developer to be able to mark each place that they expect to be null/undefined, and only those. Indeed, we believe that an unexpected null/undefined value, being a symptom of a probable bug, should be reported as a TypeError rather than swept under the rug.
balajahe
nullish coalescing — в тайпскрипте уже есть:
Хм, в JS тоже сработало
Djaler
до финальной стадии стандарта эти фичи ещё не дошли, но браузеры их уже поддерживают, это нормально
chekadee
Да есть. Просто автор некорректно написал заголовок статьи и по этой причине всё пошло
по пи@#ене так как было задумано.Статья о том, что включат в стандарт. То есть сейчас это работает, находится в стадии предложения внесения в стандарт, но может быть либо отклонено, изменено перед внесением и, наконец, внесено в стандарт.
germn Автор
> но может быть либо отклонено, изменено перед внесением и, наконец, внесено в стандарт
Боюсь, вы не правы. Это 4 этап TC39. Это означает, что ничего менять или отклонять уже не будут.
chekadee
Да, я тоже не пейсатель, поэтому затронул весь этот период от Stage 0 до внесения в стандарт и не сказал об этом :)
ilyapirogov
Как и Optional Chaining. А все остальное можно полифилами добить.
some_x
А ещё в ts есть +, -, = и много всего другого из javascript!
Вы в курсе что ts это js superset?
ratijas
Всё-равно нужно как-то завезти поддержку синтаксиса и семантики в компилятор TS.
Dartess
Насколько я понял, TS начинает активную поддержку новых proposal, начиная с их перехода на stage 3, т.е. незадолго до включения в стандарт.