С момента релиза ECMAScript2015 (также называемого ES6) JavaScript значительно изменился и улучшился. ECMAScript — это встраиваемый расширяемый не имеющий средств ввода-вывода язык программирования, используемый в качестве основы для построения других скриптовых языков, в том числе JavaScript (прим. переводчика).
Новая версия ECMAScript выпускается каждый год. Возможно, вы и не обратили внимания на то, какие функции были добавлены в последней версии ECMAScript, релиз которой мы увидели в прошлом году. Как раз на этот случай автор решил вкратце продемонстрировать фичи, добавленные в последней версии, и рассказать о новинках будущего релиза.
~
Дисклеймер:
Функции, о которых у нас пойдет речь, вероятно, войдут в следующую версию – но это не точно. Все, о чем говорится в этом посте, пока находится на третьем этапе внедрения (но с момента выхода статьи некоторые фичи перешли в Stage 4 – прим. переводчика). Проверьтеэтот репозиторий, если хотите получить более подробную информацию.
И пока мы ждем, давайте подробнее обсудим то, что интересного есть в текущей версии ECMAScript.
~
Фичи ECMAScript2019 (ES10)
1. Array.prototype.flat
Это метод, который создает новый массив со всеми элементами вложенного массива, рекурсивно объединенными с вышестоящим массивом до заданной глубины.
const array =[1,2,[3,4]];
array.flat();// [1, 2, 3, 4];
Это очень полезная фича, особенно если вы хотите выровнять вложенный массив с вышестоящим. Но если уровень вложенности вашего массива больше единицы, одноразовый вызов flat не сможет полностью выровнять массивы. Тогда для flat можно задать параметр глубины, который укажет на то, какой уровень вложенности вы хотите охватить, чтобы выровнять массивы.
Чем глубже вы погружаетесь в массив, тем больше вычислительного времени потребуется для его выравнивания. Обратите внимание, что 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 awaitECMAScript Modules (ESM) могут ожидать(await)ресурсы, заставляя другие модули, импортирующие их(import), ждать, прежде чем они начнутвыполнять их код.
Мотивация для внедрения этой функции заключалась в том, что когда вы импортируете модуль (import) с асинхронной (async) функцией, выходные данные этой асинхронной функции не определены (undefined).
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'
Эта функция идет «в комплекте» с 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 =awaitfetch();
// 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. «Конечно, все они мне не понадобятся, но некоторые из них определенно сделают мой код более элегантным», - пишет он.