Hwdtech blog
Фичи JavaScript, о которых вы должны знать, если пишете на нем каждый день
Перевели для вас статью о новой версии ECMAScript и предложениях, которые были реализованы в ней.
18-08-2020
перевод, JS, JavaScript, front-end
Время чтения: 7 минут
Сегодня переводим статью фронтенд-разработчика из Южной Кореи, он публикует статьи на Medium под ником Moon. Этот материал вышел в блоге, который называется «JavaScript in plain English», то есть «JavaScript на простом английском» (то, что надо! – примечание переводчика), вот ссылка на оригинал: Upcoming new JavaScript features You should know if you use JavaScript everyday.
С момента релиза ECMAScript2015 (также называемого ES6) JavaScript значительно изменился и улучшился. ECMAScript — это встраиваемый расширяемый не имеющий средств ввода-вывода язык программирования, используемый в качестве основы для построения других скриптовых языков, в том числе JavaScript (прим. переводчика).

Новая версия ECMAScript выпускается каждый год. Возможно, вы и не обратили внимания на то, какие функции были добавлены в последней версии ECMAScript, релиз которой мы увидели в прошлом году. Как раз на этот случай автор решил вкратце продемонстрировать фичи, добавленные в последней версии, и рассказать о новинках будущего релиза.
~
Дисклеймер:
Функции, о которых у нас пойдет речь, вероятно, войдут в следующую версию – но это не точно. Все, о чем говорится в этом посте, пока находится на третьем этапе внедрения (но с момента выхода статьи некоторые фичи перешли в Stage 4 – прим. переводчика). Проверьте этот репозиторий, если хотите получить более подробную информацию.
Обновлено: декабрь 2019 – Stage 4
Nullish coalescing Operator (нулевой оператор объединения) и Optional Chaining (опциональное связывание), а также import.meta proposal и String.prototype.replaceAll (примечание переводчика) сейчас уже на четвертой стадии, которая является финальной. Вы можете посмотреть подробности об этом здесь.

И пока мы ждем, давайте подробнее обсудим то, что интересного есть в текущей версии ECMAScript.
~
Фичи ECMAScript2019 (ES10)
1. Array.prototype.flat
Это метод, который создает новый массив со всеми элементами вложенного массива, рекурсивно объединенными с вышестоящим массивом до заданной глубины.
const array = [1, 2, [3, 4]];

array.flat(); // [1, 2, 3, 4];

Это очень полезная фича, особенно если вы хотите выровнять вложенный массив с вышестоящим. Но если уровень вложенности вашего массива больше единицы, одноразовый вызов flat не сможет полностью выровнять массивы. Тогда для flat можно задать параметр глубины, который укажет на то, какой уровень вложенности вы хотите охватить, чтобы выровнять массивы.
// Crazy example

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

crazyArray.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8];

// The parameter must be the number type

Чем глубже вы погружаетесь в массив, тем больше вычислительного времени потребуется для его выравнивания. Обратите внимание, что IE и Edge не поддерживают эту функцию.
2. Array.prototype.flatMap
Метод сначала преобразует каждый элемент с помощью функции преобразования, а затем выравнивает результат в новом массиве.
const arr = ["it's Sunny in", "", "California"];

arr.flatMap(x => x.split(" "));

// ["it's","Sunny","in", "", "California"]

Разница между flat и flatMap заключается в том, что во flatMap вы можете поместить пользовательскую функцию для управления каждым значением. Кроме того, в отличие от flat, flatMap выравнивает только массивы с уровнем вложенности 1. Возвращаемое значение должно быть типом массива. Это может быть очень полезно, если вы должны что-то сделать, прежде чем выровнять массив.

В ES10 были добавлены и другие возможности. Нажмите здесь, если вы хотите узнать больше о них.
Новые фичи на Stage 3
На третьем этапе внедрения сейчас находятся несколько интересных функций. Автор предлагает обратить внимание на некоторые из них.
1. Числовые разделители
Когда вы присваиваете переменной большое числовое значение, вас не смущает вопрос насколько оно большое и правильно ли вы его написали? Эта фича позволит вам поставить знак «подчеркивание» между цифрами, чтобы вам было проще подсчитать их количество в числе.
1_000_000_000 // Ah, so a billion

101_475_938.38 // And this is hundreds of millions

let fee = 123_00; // $123 (12300 cents, apparently)

let fee = 12_300; // $12,300 (woah, that fee!)

let amount = 12345_00; // 12,345 (1234500 cents, apparently)

let amount = 123_4500; // 123.45 (4-fixed financial)

let amount = 1_234_500; // 1,234,500

let budget = 1_000_000_000_000;

// What is the value of `budget`? It's 1 trillion!

//

// Let's confirm:

console.log(budget === 10 ** 12); // true

Каждый разработчик сам решит, использовать эту функцию или нет (разумеется, после ее релиза), но одно ясно наверняка: эта фича уменьшит вашу головную боль при определении насколько велико число.
2. Top-level await
Top-level await позволяет модулям действовать как большие асинхронные функции: с помощью top-level await ECMAScript Modules (ESM) могут ожидать (await) ресурсы, заставляя другие модули, импортирующие их (import), ждать, прежде чем они начнут выполнять их код.
Мотивация для внедрения этой функции заключалась в том, что когда вы импортируете модуль (import) с асинхронной (async) функцией, выходные данные этой асинхронной функции не определены (undefined).
/ awaiting.mjs

import { process } from "./some-module.mjs";

const dynamic = import(computedModuleSpecifier);

const data = fetch(url);

export const output = process((await dynamic).default, await data);

Представим себе два файла. Выходные данные (output) могут быть неопределенными (undefined), если они вызываются прежде чем выполнены Promises tasks.
// usage.mjs

import { output } from "./awaiting.mjs";

export function outputPlusValue(value) { return output + value }

console.log(outputPlusValue(100));

setTimeout(() => console.log(outputPlusValue(100), 1000);

usage.mjs не выполнит ни одного из утверждений в нем, пока await'ы в awaiting.mjs не дождутся разрешения своих Promises.
3. Нулевое слияние для JavaScript
Это будет одна из самых полезных функций, предложенных на Stage 3. Все мы часто писали такой код:
const obj = {

name: 'James'

};

const name = obj.name || 'Jane'; // James

Если obj.name является ложным, возвращается «Jane», а 'undefined' возвращаться не будет. Но проблема в том, что пустая строка ('') также будет считаться ложью в этом случае. Так что мы должны переписать код снова, как показано ниже.
const name = (obj.name && obj.name !== '') ? obj.name : 'Jane';
Каждый раз писать такой код – это головная боль. Эта фича позволяет нам проверять только null и undefined.
const response = {

settings: {

nullValue: null,

height: 400,

animationDuration: 0,

headerText: '',

showSplashScreen: false

}

};

const undefinedValue = response.settings.undefinedValue ?? 'some other default'; // result: 'some other default'

const nullValue = response.settings.nullValue ?? 'some other default'; // result: 'some other default'

const headerText = response.settings.headerText ?? 'Hello, world!'; // result: ''

const animationDuration = response.settings.animationDuration ?? 300; // result: 0

const showSplashScreen = response.settings.showSplashScreen ?? true; // result: false

4. Optional Chaining
Эта функция идет «в комплекте» с Nullish Coalescing for JavaScript, особенно в TypeScript. Разработчики TypeScript объявили, что они включат Nullish Coalescing for JavaScript и это proposal в свой следующий релиз, 3.7.0.
const city = country && country.city;

// undefined if city doesn't exist

Взгляните на код в данном примере. Чтобы получить «город» (city), который находится в объекте «страна» (country), мы должны сперва проверить, существует ли «страна» и существует ли «город» в «стране».

С помощью Optional Chaining этот код можно отрефакторить следующим образом:
const city = country?.city; // undefined if city doesn't exist
Эта функция кажется очень удобной и полезной в данной ситуации.
import { fetch } from '../yourFetch.js';

(async () => {

const res = await fetch();

// res && res.data && res.data.cities || undefined

const cities = res?.data?.cities;

})();

5. Promise.any
Метод Promise.any() принимает итерируемый объект содержащий объекты "обещаний" Promise. Как только одно из "обещаний"(Promise) выполнится успешно(fullfill), метод возвратит единственный объект Promise со значением выполненного "обещания". Если ни одно из "обещаний" не завершится успешно(если все "обещания" завершатся с ошибкой, т.е. rejected), тогда возвращенный объект promise будет отклонен(rejected) с одним из значений: массив содержащий причины ошибки(отклонения), или AggregateError — подкласс Error, который объединяет выброшенные ошибки вместе. По-существу, метод Promise.any() является противоположностью для Promise.all(). (цитата из документации - прим. переводчика).
Пример с async-await:
try {

const first = await Promise.any(promises);

// Any of the promises was fulfilled.

} catch (error) {

// All of the promises were rejected.

}

Пример с Promise pattern:
Promise.any(promises).then(

(first) => {

// Any of the promises was fulfilled.

},

(error) => {

// All of the promises were rejected.

}

);

Так как тут были Promise all, allSettled и race, то не было any. Таким образом, эта функция проста, но эффективна в случае необходимости.

Однако на момент написания статьи это предложение (proposal) еще не было протестировано, так что может потребоваться больше времени для принятия его в будущей версии ECMAScript.
Выводы
Stage 3 полна интересных предложений! Автор материала с нетерпением ждет их реализации в ES11 или ES12. «Конечно, все они мне не понадобятся, но некоторые из них определенно сделают мой код более элегантным», - пишет он.

Раз в месяц мы делаем рассылку с анонсом новых кейсов и статей, опубликованных на сайте.
Подпишитесь на обновления.
Гарантируем - никакого спама. Нажимая на кнопку, вы даете согласие на обработку персональных данных и соглашаетесь c политикой в отношении обработки персональных данных.