[Перевод] Возможности современного JavaScript, о которых вы могли не знать

ekqfsxt2ypt8wx9b3lhtezz6lag.jpeg

Несмотря на то, что в последние семь лет я пишу на JavaScript почти каждый рабочий день, должен признаться, что уделяю мало внимания сообщениям о нововведениях от ES. Главные возможности вроде async/await и прокси — это одно, но ещё каждый год идёт поток мелких поэтапных изменений, которые не попадают в моё поле зрения, поскольку всегда находится что-то более важное для изучения.

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

ES2015


Двоичные и восьмеричные литералы


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

Всё это может потребовать большого количества работы по скрытию/объединению двоичных чисел; мне всегда казалось, что их зря упрятали в десятичные. Именно для таких случаев в ES6 добавили формат двоичных литералов: 0b.

const binaryZero = 0b0;
const binaryOne  = 0b1;
const binary255  = 0b11111111;
const binaryLong = 0b111101011101101;


Это сильно упрощает работу с двоичными флагами:

// Pizza toppings
const olives    = 0b0001;
const ham       = 0b0010;
const pineapple = 0b0100;
const artechoke = 0b1000;

const pizza_ham_pineapple = pineapple | ham;
const pizza_four_seasons  = olives | ham | artechoke;


То же самое и с восьмеричными числами. В мире JS это нишевая возможность, но организации сетевой работы и некоторых файловых форматах они используются часто. Вы можете писать восьмеричные числа с помощью синтаксиса 0o.

Number.isNaN ()


Не путать с window.isNaN(), это новый метод с гораздо более интуитивным поведением.

У классического isNaN есть несколько интересных хитростей:

isNaN(NaN)              === true
isNaN(null)             === false
isNaN(undefined)        === true
isNaN({})               === true
isNaN('0/0')            === true
isNaN('hello')          === true


Что нам это даёт? Во-первых, ни один из этих параметров на самом деле не является NaN. Как обычно, проблема во всеми «любимом» свойстве JavaScript: приведении типов. Аргументы для window.isNaN приведены к числам с помощью функции Number.

Эту проблему решает новый статический метод Number.isNaN(). Он раз и навсегда возвращает равенство аргументов, переданных ему и NaN. Это абсолютно однозначно:

Number.isNaN(NaN)       === true
Number.isNaN(null)      === false
Number.isNaN(undefined) === false
Number.isNaN({})        === false
Number.isNaN('0/0')     === false
Number.isNaN('hello')   === false


Сигнатура: Number.isNaN : (value: any) => boolean

ES2016


Оператор возведение в степень


Время от времени такое случается, так что хорошо иметь под рукой литеральный синтаксис для возведения в степень:

2**2 === 4
3**2 === 9
3**3 === 27


Странно, но я был уверен, что в JavaScript такое уже есть. Возможно, спутал с Python.

Array.prototype.includes ()


Такое трудно было пропустить, но если в последние три года вы писали array.indexOf(x) !== -1, то возрадуйтесь новому методу includes:

[1, 2, 3].includes(2)    === true
[1, 2, 3].includes(true) === false


includes использует алгоритм Same Value Zero, который почти идентичен проверке на строгое равенство (===), за исключением того, что может обрабатывать значения NaN. Этот алгоритм тоже сравнивает объекты по ссылкам, а не содержимому:

const object1 = {};
const object2 = {};

const array = [object1, 78, NaN];

array.includes(object1) === true
array.includes(object2) === false
array.includes(NaN)     === true


includes может брать второй параметр, fromIndex, который позволяет вам предоставлять значение сдвига:

// positions   0  1  2  3  4
const array = [1, 1, 1, 2, 2];

array.includes(1, 2) === true
array.includes(1, 3) === false


Полезно.

Сигнатура: Array.prototype.includes : (match: any, offset?: Int) => boolean

ES2017


Разделяемые память и атомарные операции


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

Это две большие возможности с довольно сложными API, так что здесь я их описывать не будут. За подробностями отправляю вас к этой статье: https://www.sitepen.com/blog/the-return-of-sharedarraybuffers-and-atomics/. Еще не все браузеры поддерживают эти функции, но надеюсь, что в ближайшие пару лет ситуация улучшится.

ES2018


Золотая жила регулярных выражений


В ES2018 появилась целая россыпь новых возможностей регулярных выражений:

Lookbehind-сопоставления (сопоставление с предыдущими символами)


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

const regex = /(?<=\$)\d+/;
const text  = 'This cost $400';
text.match(regex) === ['400']


Всё дело в новой lookbehind-группе, близнеце lookahead-групп:

Look ahead:  (?=abc)
Look behind: (?<=abc)

Look ahead negative:  (?!abc)
Look behind negative: (?


К сожалению, сегодня нельзя транспилировать новый lookbehind-синтаксис под старые браузеры, так что вполне возможно, какое-то время вы сможете использовать его только в Node.

Именованные группы захвата


Теперь регулярные выражения могут выбирать подвыборки и использовать для простого парсинга. До недавнего времени мы могли ссылаться на такие фрагменты только по числам, например:

const getNameParts  = /(\w+)\s+(\w+)/g;
const name          = "Weyland Smithers";
const subMatches    = getNameParts.exec(name);

subMatches[1]     === 'Weyland'
subMatches[2]     === 'Smithers'


А теперь есть синтаксис присвоения имён этим подвыборкам (или группам записи): внутри скобок в начале ставим ?, если хотим присвоить группе имя:

const getNameParts  = /(?\w+)\s(?\w+)/g;
const name          = "Weyland Smithers";
const subMatches    = getNameParts.exec(name);

const {first, last} = subMatches.groups
first             === 'Weyland'
last              === 'Smithers'


К сожалению, сейчас это работает только в Chrome и Node.

Теперь точки могут отмечать новые строки


Нужно только проставлять флаг /s, например, /someRegex./s, /anotherRegex./sg.

ES2019


Array.prototype.flat () и flatMap ()


Я был очень рад увидеть это в MDN.

Попросту говоря, flat() преобразует многомерный массив в одномерный на заданную максимальную глубину (depth):

const multiDimensional = [
    [1, 2, 3],
   [4, 5, 6],
   [7,[8,9]]
];

multiDimensional.flat(2) === [1, 2, 3, 4, 5, 6, 7, 8, 9]


flatMap — это map, за которым идёт flat с глубиной 1. Это полезно, если нужно мапить функцию, которая возвращает массив, но при этом вам не нужно, чтобы результат представлял собой вложенную структуру данных:

const texts = ["Hello,", "today I", "will", "use FlatMap"];

// with a plain map
const mapped = texts.map(text => text.split(' '));
mapped === ['Hello', ['today', 'I'], 'will', ['use', 'FlatMap']];

// with flatmap
const flatMapped = texts.flatMap(text => text.split(' '));
flatMapped === ['Hello', 'today', 'I', 'will', 'use', 'FlatMap'];


Неограниченные перехваты


Теперь вы можете писать выражения try/catch без привязки к киданию ошибок:

try {
 // something throws
} catch {
 // don't have to do catch(e)
}


Кстати, перехваты, в которых вы не учитываете значение e, иногда называют обработкой исключений-покемонов. Потому что вы должны поймать их все!

Методы обрезки строковых значений


Незначительно, но приятно:

const padded         = '          Hello world   ';
padded.trimStart() === 'Hello world   ';
padded.trimEnd()   === '          Hello world';

© Habrahabr.ru