Недавно опциональный доступ к аттрибутам (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)


  1. balajahe
    09.01.2020 11:02
    -1

    nullish coalescing — в тайпскрипте уже есть:

    let o: any = {id: 1, name: "odin"}
    console.log(o.name1 ?? o.name)

    Хм, в JS тоже сработало


    1. Djaler
      09.01.2020 11:39

      до финальной стадии стандарта эти фичи ещё не дошли, но браузеры их уже поддерживают, это нормально


    1. chekadee
      09.01.2020 13:25

      Да есть. Просто автор некорректно написал заголовок статьи и по этой причине всё пошло по пи@#е не так как было задумано.
      Статья о том, что включат в стандарт. То есть сейчас это работает, находится в стадии предложения внесения в стандарт, но может быть либо отклонено, изменено перед внесением и, наконец, внесено в стандарт.


      1. germn Автор
        09.01.2020 13:27

        > но может быть либо отклонено, изменено перед внесением и, наконец, внесено в стандарт

        Боюсь, вы не правы. Это 4 этап TC39. Это означает, что ничего менять или отклонять уже не будут.


        1. chekadee
          09.01.2020 13:33

          Да, я тоже не пейсатель, поэтому затронул весь этот период от Stage 0 до внесения в стандарт и не сказал об этом :)


    1. ilyapirogov
      09.01.2020 20:52

      nullish coalescing — в тайпскрипте уже есть

      Как и Optional Chaining. А все остальное можно полифилами добить.


    1. some_x
      10.01.2020 07:13

      А ещё в ts есть +, -, = и много всего другого из javascript!
      Вы в курсе что ts это js superset?


      1. ratijas
        10.01.2020 13:44

        Всё-равно нужно как-то завезти поддержку синтаксиса и семантики в компилятор TS.


    1. Dartess
      11.01.2020 12:48

      Насколько я понял, TS начинает активную поддержку новых proposal, начиная с их перехода на stage 3, т.е. незадолго до включения в стандарт.


  1. vladimirx
    09.01.2020 11:37
    +1

    Хоть мне и не лень написать код без Optional Chaining, но эта фича меня всегда радовала в других языках :) Радует как JS развивается в последние года.


  1. Eldhenn
    09.01.2020 13:41

    > Optional Chaining

    Я джвадцать джва года ждал эту фичу!


    1. BerkutEagle
      09.01.2020 14:52

      Я джвадцать джва года ждал эту

      Uncaught TypeError: Cannot read property 'feature' of undefined


  1. gdt
    09.01.2020 16:30
    +1

    Ещё несколько лет, и JS превратится в удобный язык :)


  1. botyaslonim
    09.01.2020 17:19
    +1

    Последние два пункта да, постоянная головная боль. Ждём-ждём!


  1. OlegSchwann
    09.01.2020 20:29

    > Попытка сложить BigInt с числом 4n + 2 приводит к исключению.
    Удивляет, что от первоначальной философии языка отошли.
    Впрочем, это хорошо и удобно для всех, кроме участников www.dwitter.net.


    1. ReklatsMasters
      11.01.2020 02:07

      Вообще это правильно что прекратили порочную практику сложения тёплого с мягким.


    1. Nookie-Grey
      12.01.2020 22:56

      да, чёт странно, можно скаладывать всё что угодно, кроме BigInt, это явно не javascript


  1. Format-X22
    10.01.2020 01:32

    А в NodeJS удобно использовать этот сайт — https://node.green/


    Как только LTS версия ноды делает шаг вперёд — самое время проверять чего добавили и изучать если что было пропущено.


    BigInt уже там давно и лично продакшн код с ним писал, а вот например штуки с опциональным чейнингом посвежее.


    А ещё можно наблюдать как в язык завозят приватные свойства и, судя по всему, скоро завезут и приватные методы.


    Вообще там есть чего почитать и будущее прикинуть, один из мной любимых сайтов по фичам и развитию языка уже многие годы.


    1. ReklatsMasters
      11.01.2020 02:09

      В 13 версию уже видел pr с приватными свойствами, выглядит классно, хоть я и был против такого синтаксиса.


  1. iluxa1810
    10.01.2020 09:09

    Ура! Наконец-то на JS можно рассчитывать гос.долг США без переполнений


  1. sanchezzzhak
    10.01.2020 10:26

    Сожалению Nullish Coalescing, Optional Chaining работают немного не так, как хочется.
    Проверка идет только последнего элемента на (null, void 0), а не всей цепочки.


    1. ratijas
      10.01.2020 13:47
      +1

      В каких жизненных ситуациях вы хотели бы, чтобы одним оператором делалось более одного действия сразу? О_о


    1. Aingis
      10.01.2020 19:22

      В смысле «не всей цепочки»? Что мешает использовать Optional chaining во всей цепочке?


      someObj.?property.?mayBeUndefined ?? 'Response'


    1. Nookie-Grey
      12.01.2020 23:07

      Optional 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.