ECMAScript — стандарт, на котором основан JavaScript, его часто называют ES.
ES3, ES5, ES6, ES7, ES8, ES2015, ES2016, ES2017, ES2018, ES2019, ECMAScript 2015, ECMAScript 2016, ECMAScript 2017, ECMAScript 2018, ECMAScript 2019 — как разобраться во всем этом?
ECMAScript (/ˈɛkməskrɪpt/) (или ES) является языком программирования общего назначения , стандартизирован ассоциацией Ecma International согласно документу ECMA-262 . Это стандарт JavaScript, предназначенный для обеспечения взаимодействия веб-страниц в разных веб-браузерах.
Историческая справка
Ecma International — основанная в 1961 году ассоциация, деятельность которой посвящена стандартизации информационных и коммуникационных технологий. Изначально ассоциация называлась ECMA — European Computer Manufacturers Association, однако она сменила название в 1994 году в связи с глобализацией деятельности. Вследствие этого название Ecma перестало быть аббревиатурой и больше не пишется заглавными буквами.
Когда JavaScript был создан, он был представлен Netscape и Sun Microsystems для Ecma, и они дали ему имя ECMA-262 (псевдоним ECMAScript).
До ES2015 спецификации ECMAScript обычно назывались их редакцией. Таким образом, ES5 является официальным названием обновления спецификации ECMAScript, опубликованной в 2009 году.
В процессе разработки ES2015, название было изменено с ES6 на ES2015, но мир всё еще называет релизы ES номером издания.
Также комитетом было принято решение о ежегодном пересмотре и выпуске стандарта, в результате, начиная с 2015 года, мы каждый год получаем новый стандарт ECMAScript.
Для лучшего понимания исторической последовательности развития стандартов JavaScript смотрите табличку ниже:
ECMAScript2015 (ES6)
ES5 разрабатывался 10 лет, с 1999 по 2009 год и был полон существенных изменений, поэтому он считает фундаментальной версией стандарта ECMAScript.
Стрелочные функции
let func = (arg1, arg2, arg3) => expression
Более интуитивное управление текущим контекстом объекта
this
Объявление переменных с помощью
let
иconst
Промисы (Promises)
const waitFunc = () =>
new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
});
waitFunc().then(() => {
console.log("I promised to run after 1s");
});
Генераторы (Generators)— это особый тип функций, которые могут приостанавливать своё выполнение, возвращать промежуточный результат и далее возобновлять его позже, в произвольный момент времени, что позволяет запускать другой код в момент приостановления функции. Для объявления генератора используется синтаксическая конструкция:
function*
(функция со звёздочкой).Шаблонные литералы (Template Literals) — это новый синтаксис для создания строк, обеспечивающий способ встраивания выражений в строки, используя синтаксис
${имя_переменной}
:
const name = "Irina";
const string = `Hey ${name}`; // Hey Irina
Параметры по умолчанию в функциях:
const sayMyName = function (name = "Irina") {
console.log(name);
};
sayMyName(); // Irina
Spread/Rest синтаксис (
...
) в параметрах функций:
// spread
Math.max(...[2,100,1,6,43]) // 100
// rest
function print(format, ...params) {
console.log('params: ', params);
console.log('format: ', format);
}
print('hello', 'adrian', 321, Math.PI);
Деструктуризация (Destructuring Assignment) — синтаксис, позволяющий извлекать данные из массивов и объектов.
const user = {firstName: 'Adrian', lastName: 'Mejia'};
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
console.log(getFullName(user));
Symbol — это уникальный и неизменяемый тип данных, который может быть использован как идентификатор для свойств объектов.
var sym1 = Symbol();
var sym2 = Symbol("foo");
Symbol("foo") === Symbol("foo"); // false
Цикл
for...of
выполняет цикл обхода любых итерируемых объектов (включаяArray
,Map
,Set
, объект аргументов и т.д.), и имеет возможность разрываbreak;
Итерируемым объектом является любой объект, который реализует интерфейс Iterable, то есть у которого есть метод Symbol.iterator, возвращающий объект с методом next().
Map
иSet
(и их соответствующие аналоги WeakMap и WeakSet с поддержкой сборки мусора) — являются официальными реализациями двух очень популярных структур данных:
Map
содержит пары ключ-значение и сохраняет порядок вставки. Любое значение может быть использовано в качестве ключа.
Set
позволяет сохранять уникальные значения любого типа.
Классы:
Конструктор — специальный метод
constructor
, который вызывается, когда класс инициализируются с помощьюnew
. Родительскийconstructor
наследуется автоматически, если у потомка нет своего методаconstructor
. Если же потомок имеет свойconstructor
, то, чтобы унаследовать конструктор родителя нужно использоватьsuper()
с аргументами для родителя.super()
— используется для вызова функций, принадлежащих родителю объекта;Getters and setters:
get
связывает свойство объекта с функцией, которая вызывается при обращении к этому свойству
class Person {
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
set
связывает свойство объекта с функцией, он будет вызываться при попытке установить это свойство
class Person {
set age(years) {
this.theAge = years;
}
}
Модули:
Импорт модулей
import defaultExport from "module-name";
import * as name from "module-name";
import * from 'module-name';
import { namedExport } from 'module-name';
import "module-name";
Экспорт модулей
export { name1, name2, …, nameN };
export default выражение;
export * from …;
export default function (…) { … };
Расширенные литералы объекта:
Более простой синтаксис для переменных, если они имеют одинаковые имена:
// вместо этого:
const name = "Irina";
const x = {
name: name,
};
// можно писать так:
const name = "Irina";
const x = {
name,
};
super()
const person = { name: "Irina", say: () => "Hello " };
const developer = {
__proto__: person,
say() {
return super.say() + this.name;
},
};
developer.say(); // Hello Irina
Динамические имена свойств объектов:
const myObj = {
["some" + "thing"]: "123",
};
myObj.something; // 123
Новые строковые методы:
repeat()
повторяет строку указанное количество раз;codePointAt()
возвращает не отрицательное целое число, которое является закодированным в UTF-16 значением кодовой точки.
Новые методы объекта:
Object.is()
определяет, являются ли два значения одинаковымиvar isSame = Object.is(value1, value2)
;Object.assign()
используется для поверхностного копирования всех свойств объекта в целевой объектObject.assign(target, ...sources)
;-
Object.setPrototypeOf
устанавливает прототип объектаObject.setPrototypeOf(obj, prototype)
.
ECMAScript2016 (ES7)
Cодержит всего две функции:
Array.prototype.includes()
— проверяет, содержит ли массив элемент, возвращая в зависимости от этогоtrue
илиfalse
.Оператор возведения в степень
**
— является эквивалентомMath.pow()
ECMAScript2017 (ES8)
String.prototype.padStart()
иString.prototype.padEnd()
— позволяют присоединять к строкам, в их начало или конец, некоторое количество символов для дополнения строк до заданной длины. Это полезно, если нужно выровнять текст, например, при выводе в консоль.Object.values()
— возвращает массив, содержащий все значения свойств объекта, исключая любые значения в цепочке прототипов.Object.entries()
— возвращает массив, содержащий все собственные свойства объекта, в виде массива пар[key, value]
.getOwnPropertyDescriptors()
— принимает объект и возвращает все собственные дескрипторы свойств данного объекта (включая данные о геттерах и сеттерах).«Висячие» запятые в параметрах функций — позволяет ставить запятую после последнего параметра функции.
const someFunc = (var1, var2,) => {
//...
};
someFunc("test2", "test2",);
async/await или асинхронные функции — абстракция более высокого уровня по сравнению с промисами. Асинхронные функции позволяют избавиться от так называемого «ада коллбэков» и улучшить внешний вид и читаемость кода.
Разделяемая память (shared memory) и атомарные операции (atomics) — это функционал, который является основным улучшением движков JS.
Основная идея состоит в том, чтобы привнести в JavaScript какую-то многопоточность, для того чтоб разработчики JS могли писать высокопроизводительные параллельные программы и управлять памятью самостоятельно, а не позволять это делать движку JS.
Для этого задействуется новый тип глобального объекта SharedArrayBuffer
, который хранит данные в общем пространстве памяти. Эти данные могут быть разделены между основным потоком JS и потоками web-workers.
WebWorkers предлагают протокол обмена сообщениями через события. Начиная с ES2017, мы можем создавать массив разделяемой памяти между web-workers и их создателями, используя SharedArrayBuffer
.
Поскольку неизвестно, сколько времени занимает запись в разделяемую часть памяти, мы используется Atomics — способ удостоверится, что при чтении значения, любой вид операции записи завершен.
Более подробно об этом можно почитать в статье
ECMAScript2018 (ES9)
Асинхронная итерация
for-await-of
— позволяет вызывать асинхронные функции, которые возвращают промис (или массив с кучей промисов) в цикле:
const promises = [
new Promise(resolve => resolve(1)),
new Promise(resolve => resolve(2)),
new Promise(resolve => resolve(3))];
async function testFunc() {
for await (const obj of promises) {
console.log(obj);
}
}
testFunc(); // 1, 2, 3
Promise.prototype.finally()
— позволяет запускать код, независимо от успешного или неуспешного выполнения промиса:
fetch(myRequest)
.then(res => res.json())
.catch(error => console.error(error))
.finally(() => console.log("finished"));
Spread/Rest операторы для свойств объекта:
spread
— позволяет создавать новый объект путем объединения свойств объекта, переданного после оператора...
:
const arr = { first, second, ...others };
arr; //{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }
rest
— cинтаксис для rest оператора выглядит таким же как и для spread оператора, однако он используется для деструктуризации массивов и объектов. Фактически, rest оператор противоположен spread оператору: последний раскладывает массив на элементы, тогда как первый собирает много элементов в один.
const { first, second, ...others } = {
first: 1,
second: 2,
third: 3,
fourth: 4,
fifth: 5};
first; // 1
second; // 2
others; // { third: 3, fourth: 4, fifth: 5 }
Улучшения регулярных выражений:
Ретроспективные проверки (Lookbehind Assertion)
Позитивная ретроспективная проверка: (?<=Y)X
, ищет совпадение с X
при условии, что перед ним ЕСТЬ Y
.
Негативная ретроспективная проверка: (?<!Y)X
, ищет совпадение с X
при условии, что перед ним НЕТ Y
.
Юникодные свойства \p{…}
Несмотря на то, что это часть стандарта с 2018 года, юникодные свойства не поддерживаются в Firefox до 78 версии и в Edge до 79 версии.
Каждому символу в кодировке Юникод соответствует множество свойств, описывающих к какой «категории» относится символ и содержащих различную информацию о нём.
Например, свойство Letter
у символа означает, что это буква какого-то алфавита, причём любого. А свойство Number
означает, что это цифра – арабская или китайская, и т.п, на каком-то из языков.
В регулярном выражении можно искать символ с заданным свойством, указав его в \p{…}
.
Например, \p{Letter}
обозначает букву в любом языке. Также можно использовать запись \p{L}
, так как L
– это псевдоним Letter
. Существуют короткие записи почти для всех свойств.
Несмотря на то, что это часть стандарта с 2018 года, юникодные свойства не поддерживаются в Firefox до 78 версии и в Edge до 79 версии.
Существует библиотека XRegExp, которая реализует «расширенные» регулярные выражения с кросс-браузерной поддержкой юникодных свойств.
В сложных регулярных выражениях запоминать группы по номерам затруднительно. Гораздо лучше — давать скобкам имена. Это делается добавлением ?<name>
непосредственно после открытия скобки.
Например, поищем дату в формате «день-месяц-год»:
let dateRegexp = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;
let str = "2021-03-30";
let groups = str.match(dateRegexp).groups;
alert(groups.year); // 2021
alert(groups.month); // 03
alert(groups.day); // 30
Флаг
s
— включает режим «dotall», при котором точка.
может соответствовать символу перевода строки\n
ECMAScript2019 (ES10)
Array.flat()
— возвращает новый массив, в котором все элементы вложенных подмассивов были рекурсивно “подняты” на указанный уровень глубины (depth).
Вызов flat()
без каких-либо аргументов сглаживает только первый уровень глубины. Можно указать необязательный аргумент глубины или вызвать функцию последовательно.
var arr1 = [1, 2, [3, 4]];
arr1.flat(); // [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(); // [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2); // [1, 2, 3, 4, 5, 6]
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Array.flatMap()
— сначала применяет функцию к каждому элементу, а затем преобразует полученный результат в плоскую структуру и помещает в новый массив. Это идентичноmap
функции, с последующим применением функцииflat
с параметром depth равным 1, ноflatMap
часто бывает полезным, так как работает немного более эффективно.String.trimStart()
иString.trimEnd()
— удаляют пробелы в начале и в конце строки соответственно.Необязательная привязка
catch
— позволяет разработчикам использоватьtry/catch
без параметраerror
внутри блокаcatch
.Object.fromEntries()
— создает объект или преобразует пары ключ-значение в объект. Он принимает только итерируемый объект:Object.fromEntries(iterable)
.Symbol.description
— read-only cвойство - строка, возвращающая необязательное описание объектов Symbol
console.log(Symbol('desc').description); // expected output: "desc"
Хорошо сформированный JSON.stringify()
Исправленный вывод JSON.stringify()
при обработке суррогатных кодовых точек UTF-8 (от U+D800 до U+DFFF).
Перед этим изменением вызов JSON.stringify()
возвращал некорректный символ Unicode («�»).
Теперь эти суррогатные кодовые точки можно безопасно представить в виде строк, используя JSON.stringify()
, и преобразовать обратно в их исходное представление, используя JSON.parse()
.
JSON.stringify('\uD800');
> '"�"'
JSON.stringify('\uD800');
> '"\\ud800"'
Корректировки метода
Function.prototype.toString()
Функции всегда имели метод экземпляра toString()
, который возвращает строку, содержащую код функции.
ES2019 ввел изменение в возвращаемое значение, чтобы избежать удаления комментариев и других символов, таких как пробел, точно представляющих функцию в том виде, как она была определена.
Например, для функции:
function /* a comment */ name() {}
// Поведение было таким:
name.toString();
// "function name() {}"
// Стало таким:
name.toString();
// "function /* a comment */ name () {}"
ECMAScript2020 (ES11)
String.matchAll
— возвращает итератор, который в свою очередь возвращает все совпадающие группы одну за другой.
const str = "abc";
const regexp = /[a-c]/g;
const iterator = str.matchAll(regexp);
for (result of iterator) {
console.log(result);
}
// ["a", index: 0, input: "abc", groups: undefined]
// ["b", index: 1, input: "abc", groups: undefined]
// ["c", index: 2, input: "abc", groups: undefined]
Динамический импорт — даёт возможность динамически импортировать файлы JS в виде модулей.
let modulePath = prompt("Какой модуль загружать?");
import(modulePath)
.then(obj => <объект модуля>)
.catch(err => <ошибка загрузки, например если нет такого модуля>)
BigInt — это специальный числовой тип, который предоставляет возможность работать с целыми числами произвольной длины.
Т.е. это способ представлять целые числа больше pow(2, 53) - 1
, максимального числа, которое JavaScript может надежно представить с Number примитивом.
Чтобы создать значение типа BigInt
, необходимо добавить n
в конец числового литерала или вызвать функцию BigInt
, которая создаст число типа BigInt
из переданного аргумента. Аргументом может быть число, строка и др.
const bigint = 1234567890123456789012345678901234567890n;
const sameBigint = BigInt("1234567890123456789012345678901234567890");
const bigintFromNumber = BigInt(10); // то же самое, что и 10n
Promise.allSettled
— возвращает промис, который исполняется когда все полученные промисы завершены (выполнены успешно или отклонены), содержащий массив результатов исполнения полученных промисов.globalThis
В JavaScript всегда есть один большой объект контекста, который содержит всё. Традиционно в браузерах это window
. Но если попытаться получить к нему доступ в Node, то будет ошибка. В Node нет глобального объекта window
— вместо этого есть объект global
. С другой стороны, в WebWorkers нет доступа к window
, но вместо этого есть self
.
globalThis
всегда ссылается на глобальный объект, независимо от того, где мы выполняем свой код.
for-in mechanics — стандарт в каком порядке цикл
for (x in y)
должен выполняться.Optional chaining — Оператор опциональной последовательности(
?.
)
Призван сделать код короче, при работе со вложенными объектами и проверкой на undefined
.
const car = {};
const color = car?.color;
const colorName = car?.color?.name;
Nullish coalescing — Оператор нулевого слияния (
??
)
Возвращает значение правого операнда когда значение левого операнда равно null
или undefined
, в противном случае будет возвращено значение левого операнда.
const foo = null ?? 'default string';
console.log(foo);
// expected output: "default string"
const baz = 0 ?? 42;
console.log(baz);
// expected output: 0
Module namespace exports
export * as utils from "./utils.mjs";
// эквивалентно следующему:
import * as utils from "./utils.mjs";
export { utils };
ECMAScript2021 (ES12)
P.S. Данный материал был написан в начале 2021 года, для просмотра актуальной информации читайте вторую часть моей статьи :)
Ожидается, что версия ECMAScript 2021 будет выпущена в июне 2021 года. Вот некоторые из функций, которые могут оказаться в ES2021 (ES12). Список подготовлен на основе ECMAScript Proposals и новых функций, выпущенных движком Google Chrome V8.
Все функции, перечисленные ниже, на момент написания поддерживаются в сборке Google Chrome Canary (версия браузера Google Chrome, поддерживающая экспериментальные возможности).
String.prototype.replaceAll()
заменяет все вхождения строки другим строковым значением.Приватные методы — могут быть доступны только внутри класса, в котором они определены. Имена приватных методов начинаются с символа #.
class Person {
#setType() {
console.log("I am Private");
}
show() {
this.#setType();
}
}
const personObj = new Person();
personObj.show(); // "I am Private";
personObj.setType(); // TypeError: personObj.setType is not a function
Приватные аксессоры
class Person {
get name() { return "Backbencher" }
set name(value) {}
get #age() { return 42 }
set #age(value) {}
}
const obj = new Person();
console.log(obj.name); // "Backbencher" console.log(obj.age); // undefined
WeakRef — слабые ссылки (Weak References). В основном слабые ссылки используются для реализации кэшей или маппингов больших объектов. В таких сценариях мы не хотим удерживать большое количество памяти надолго, сохраняя редко используемый кэш или маппинг. Мы можем разрешить сборку мусора для памяти в ближайшее время, а позже, если она нам снова понадобится, мы можем создать свежий кэш.
Финализаторы (FinalizationRegistry)—это дополнительная функция WeakRef, позволяющая регистрировать коллбеки, которые будут вызываться после того, как объект был забран сборщиком мусора.
Promise.any()
— успешно завершается, если успешно завершился любой из предоставленных в качестве аргументов промис.
Если ни один из промисов не завершится успешно Promise.any()
сгенерирует исключение AggregateError. Нам нужно поймать это исключение и обработать.
Оператор логического присваивания (
&&=
,||=
,??=
) — объединяет логические операции (&&
,||
или??
) с присваиванием.
const x = 1;
const y = 2;
const z;
x &&= y; // 2
x ||= y; // 1
z ??= y; // 2
При написании статьи использованы следующие ресурсы: