ECMAScript 2015, 2016, 2017, 2018, 2019, 2020, 2021

ECMAScript — стандарт, на котором основан JavaScript, его часто называют ES.

57d1c4bfc6cb4f883ae80fd09aba0c1f.png

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 смотрите табличку ниже:

Даты релиза различных версий JavaScript

Даты релиза различных версий 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
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);
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 позволяет сохранять уникальные значения любого типа.

Классы:

  • Конструктор — специальный метод 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,
};
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
fetch(myRequest)  
  .then(res => res.json())  
  .catch(error => console.error(error))  
  .finally(() => console.log("finished"));

Spread/Rest операторы для свойств объекта:

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 }

Улучшения регулярных выражений:

Позитивная ретроспективная проверка:  (?<=Y)X, ищет совпадение с X при условии, что перед ним ЕСТЬ Y.

Негативная ретроспективная проверка:  (?, ищет совпадение с X при условии, что перед ним НЕТ Y.

Несмотря на то, что это часть стандарта с 2018 года, юникодные свойства не поддерживаются в Firefox до 78 версии и в Edge до 79 версии.

Каждому символу в кодировке Юникод соответствует множество свойств, описывающих к какой «категории» относится символ и содержащих различную информацию о нём.

Например, свойство Letter у символа означает, что это буква какого-то алфавита, причём любого. А свойство Number означает, что это цифра — арабская или китайская, и т.п, на каком-то из языков.

В регулярном выражении можно искать символ с заданным свойством, указав его в \p{…}.

Например,  \p{Letter} обозначает букву в любом языке. Также можно использовать запись \p{L}, так как L — это псевдоним Letter. Существуют короткие записи почти для всех свойств.

Несмотря на то, что это часть стандарта с 2018 года, юникодные свойства не поддерживаются в Firefox до 78 версии и в Edge до 79 версии.

Существует библиотека XRegExp, которая реализует «расширенные» регулярные выражения с кросс-браузерной поддержкой юникодных свойств.

В сложных регулярных выражениях запоминать группы по номерам затруднительно. Гораздо лучше — давать скобкам имена. Это делается добавлением ? непосредственно после открытия скобки.

Например, поищем дату в формате «день-месяц-год»:

let dateRegexp = /(?[0-9]{4})-(?[0-9]{2})-(?[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

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() при обработке суррогатных кодовых точек UTF-8 (от U+D800 до U+DFFF).

Перед этим изменением вызов JSON.stringify() возвращал некорректный символ Unicode (»�»).

Теперь эти суррогатные кодовые точки можно безопасно представить в виде строк, используя JSON.stringify(), и преобразовать обратно в их исходное представление, используя JSON.parse().

JSON.stringify('\uD800');
> '"�"'

JSON.stringify('\uD800');
> '"\\ud800"' 

Функции всегда имели метод экземпляра toString(), который возвращает строку, содержащую код функции.

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

Например, для функции:

function /* a comment */ name() {}

// Поведение было таким:
name.toString();
// "function name() {}"

// Стало таким:
name.toString();
// "function /* a comment */ name () {}"

ECMAScript2020 (ES11)

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]
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;

Возвращает значение правого операнда когда значение левого операнда равно 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

При написании статьи использованы следующие ресурсы:

  1. Официальный сайт ecma-international

  2. Справочник по JavaScript

  3. Полное руководство по ECMAScript

  4. Обзор новшеств ECMAScript 2016, 2017, и 2018 с примерами

  5. Что нового в JavaScript ES2019

  6. Нововведения JavaScript ES2020 с простыми примерами

  7. Новые возможности ES2021 / ES12

© Habrahabr.ru