Руководство по ES6
Узнайте всё о ECMAScript и функциях, добавленных в ES2015 (ES6).
JavaScript · 04.04.2019 · читать 8 мин · Автор: Alexey Myzgin
- Введение
- Стрелочные функции
- Новая область this
- let и const
- Promises
- Generators
- Классы
- Конструктор
- super
- Getters and setters
- Импорт модулей
- Экспорт модулей
- Более простой синтаксис
- Прототип
- super()
- Динамические свойства
ECMAScript — это стандарт, на котором основан JavaScript, и его часто называют ES.
ECMAScript 2015, также известный как ES6, является фундаментальной версией стандарта ECMAScript.
Опубликованный через 4 года после последней версии стандарта ECMAScript 5.1, он также обозначил переход с номера издания на номер года.
Поэтому его следует называть не ES6 (хотя все называют его таковым), а ES2015.
ES5 разрабатывался 10 лет, с 1999 по 2009 год, и как таковой он также был фундаментальным и крайне важным пересмотром языка.
Поскольку этот длинный промежуток времени прошел между ES5.1 и ES6, выпуск полон важных новых функций и существенных изменений в рекомендуемых лучших практиках разработки программ на JavaScript. Чтобы понять, насколько фундаментален ES2015, просто имей в виду, что в этой версии документ с техническими характеристиками увеличился с 250 страниц до 600.
В этой статье описаны наиболее важные изменения .
С момента появления стрелочных функций, последние изменили то, как выглядит и работает большая часть JavaScript кода.
Визуально, это простое и долгожданное изменение. Например функция:
const name = function name() console.log("my name is Alex"); >;
const name = () => console.log("my name is Alex"); >;
Если тело функции является однострочным, то можно убрать фигурные скобки:
const name = () => console.log("my name is Alex");
Также, если у тебя только один параметр, то можно убрать круглые скобки:
const name = (param) => console.log(`my name is $param>`);
Это не серьезное изменение, обычные функции продолжают работать так же, как и раньше.
Более подробно о стрелочных функциях здесь.
Новая область this
Значение this определяется тем, каким образом вызвана функция. В JavaScript ключевое слово this относится к объекту, к которому оно принадлежит.
Оно имеет разные значения в зависимости от того, где используется:
- в методе, this относится к объекту, по отношению к которому вызван метод;
- в глобальном контексте выполнения this ссылается к глобальному объекту;
- в функции, значение this зависит от того, каким образом вызвана функция;
- в обычной функции this относится к глобальному объекту;
- в стрелочных функциях, this привязан к окружению, в котором была создана функция;
- в строгом режиме функции, this не определен, если предварительно он не был установлен в контексте исполнения;
- в событии this относится к элементу, который получил событие;
- такие методы, как call() и apply() могут ссылать this на любой объект.
Есть несколько способов объявить переменные в ES6: объявить переменные с помощью var (который мы всегда использовали), плюс сейчас мы можем использовать let и const .
Эти два способа имеют некоторые атрибуты, которые будут полезны для нас при создании переменных. Но давай сделаем краткий обзор, как отличаются var, let и const.
Во-первых, переменные var могут быть переопределены или обновлены. Например:
var width = 100; console.log(width); // 100 width = 200; console.log(width); // 200
Также они не будут “кричать” о создании одного и того же имени переменной дважды в одной и той же области видимости, так как переменные var могут быть обновлены или переопределены.
Также важно помнить, как переменные var определены. По сути, scoping или область видимости означает «Где эти переменные доступны для меня?». В случае переменных var они являются областью действия функции, что означает, что они доступны только внутри функции, в которой они созданы. Однако, если они не объявлены в функции, то они имеют глобальную область видимости и доступны во всем окне.
var — традиционно функциональная область.
let — это новое объявление переменной, которая имеет ограниченую область видимости.
Это означает, что объявление переменных let в цикле for , внутри блока if или в обычном блоке, не позволит этой переменной «покинуть» блок, в то время как var поднимется до определения функции.
const похож на let , но он неизменный, константа (почти!). Если ты попытаешься обновить его то это не сработает, потому что ты не можешь обновить переменную const , так как let .
Ты можешь решить что const — неизменен. Например, если у тебя есть объект:
const person = name: "Alex", age: 28, >;
и ты попытаешься обновить переменную const , набрав person = < name: 'Alexey' >, то у тебя не получится. Однако свойства переменной const могут измениться. Это потому, что весь объект не является неизменным. Его просто нельзя переназначить полностью. Например:
const person = name: "Alex", age: 28, >; person.name = "Alexey";
Это сработает, но ты не можешь перезаписать всю переменную.
Promises (далее по тексту — “промисы”) позволяют нам устранить известный «callback hell» или «ад обратных вызовов», хотя они внесли небольшие усложнения (которые были решены в ES2017 с помощью async — конструкции более высокого уровня).
Промисы использовались разработчиками JavaScript задолго до ES2015, только с помощью множеств различных библиотек, а стандарт нашел общий язык между ними.
Используя промис, ты можешь переписать этот код
setTimeout(function () console.log("I promised to run after 1s"); setTimeout(function () console.log("I promised to run after 2s"); >, 1000); >, 1000);
const wait = () => new Promise((resolve, reject) => setTimeout(resolve, 1000); >); wait() .then(() => console.log("I promised to run after 1s"); return wait(); >) .then(() => console.log("I promised to run after 2s"));
Промис может быть выполнен успешно — resolved или отклонен — rejected . Когда успешно выполняешь промис, запускается then() , а когда отклоняешь запускается catch() . Обычно, внутри твоего промиса, у тебя есть какая-то логика, которая решает, будете ли промис отклонен или выполнен. Он позволяет обрабатывать результаты асинхронных операций так, как если бы они были синхронными. Вместо конечного результата асинхронного метода возвращается промис (обещание) получить результат в какой-то момент в будущем.
Generators — это особый тип функций, который дает возможность приостановить свое выполнения, а потом позже возобновить. Это позволяет запускать другой код в момент приостановления функции. function со звёздочкой function* определяет функцию-генератор.
Традиционно JavaScript — единственный основной язык с наследованием на основе прототипов. ES2015 представил классы, которые являются просто синтаксическим сахаром для внутренней работы, что сильно изменил то, как мы строим программы на JavaScript.
Теперь наследование очень простое и напоминает другие языки объектно-ориентированного программирования:
class Person constructor(name) this.name = name; > hello() return "Hello, I am " + this.name + "."; > > class Developer extends Person hello() return super.hello() + " I am a Frontend Developer."; > > const me = new Developer("Alex"); console.log(me.hello()); // Hello, I am Alex. I am a Frontend Developer.
Классы не имеют явных объявлений переменных класса, но ты должен инициализировать любую переменную в конструкторе.
Классы имеют специальный метод с именем constructor , который вызывается, когда класс инициализируются с помощью new . Родительский constructor наследуется автоматически, если у потомка нет своего метода constructor . Если же потомок имеет свой constructor , то, чтобы унаследовать конструктор родителя нужно использовать super() с аргументами для родителя.
Слово super используется для вызова функций, принадлежащих родителю объекта.
Getters and setters
Геттеры могут быть объявлены как:
class Person get fullName() return `$this.firstName> $this.lastName>`; > >
get связывает свойство объекта с функцией, которая вызывается при обращении к этому свойству.
Сеттеры пишутся так же:
class Person set age(years) this.theAge = years; > >
set связывает свойство объекта с функцией, он будет вызываться при попытке установить это свойство.
До ES2015 существовало не менее 3-х основных модулей, конкурирующих со стандартами, которые разделили сообщество:
ES2015 стандартизировал их в общий формат.
import используется для импорта ссылок на значения, экспортированные из внешнего модуля, и осуществляется через конструкцию import . from . :
import defaultExport from "module-name"; import * as name from "module-name"; import * from 'module-name' import namedExport > from 'module-name' import "module-name";
Экспорт используется для экспорта функций, объектов. Ты можешь писать модули и экспортировать что угодно в другие модули, используя ключевое слово export :
export name1, name2, …, nameN >; export default выражение; export * from …; export default function (…) … >;
Шаблонные литералы (Template Literals)
Шаблонные литералы — это новый синтаксис для создания строк:
const aString = `A string`;
Они обеспечивают способ встраивания выражений в строки, эффективно интерполируя значения, используя синтаксис $ :
const name = "Alex"; const string = `Hey $name>`; // Hey Alex
Ты также можешь выполнять более сложные выражения:
const string = `total count is: $1 + 2 + 3>`; const string2 = `result is $foo() ? "x" : "y">`;
также строки могут занимать несколько строк:
const string3 = `Hey this string is awesome!`;
Параметры по умолчанию
Функции теперь поддерживают параметры по умолчанию:
const sayMyName = function (name = "Alex") console.log(name); >; sayMyName(); // Alex
Если при вывозе функции name() мы не передали аргумент, то тогда она возьмет значение по умолчанию «Alex» .
Оператор распространения spread
Ты можешь расширить или объединить массив, объект или строку, используя оператор распространения .
Начнем с массива.
У нас есть один массив const a = [1, 2, 3] который мы хотим модифицировать, добавив несколько значений:
const a = [1, 2, 3]; const b = [. a, 4, 5, 6]; // [1, 2, 3, 4, 5, 6]
Теперь у нас новый массив b который содержит все значения a , плюс свои.
Также можно создать копию массива:
const c = [. a];
spread также работает и для объектов. Клонировать объект можно так:
const newObj = . oldObj >;
Используя строки, оператор распространения spread создает массив с каждым символом в строке:
const hey = "hey"; const arrayized = [. hey]; // ['h', 'e', 'y']
У этого оператора есть несколько довольно полезных свойств. Наиболее важным из них является возможность очень просто использовать массив в качестве аргумента функции:
const f = (one, two) => console.log(one, two); // 1, 2 >; const a = [1, 2]; f(. a);
Деструктуризация — это выражение JavaScript, которое позволяет нам извлекать данные из массивов и объектов.
Например, когда с данного объекта нам нужно извлечь только некоторые значения и поместить их в переменные, мы можем сделать это так:
const person = firstName: "Alex", dev: true, age: 28, >; const name = person.firstName; const age = person.age; console.log(name, age); // Alex, 28
Но тогда у нас выходит код повторяющийся снова и снова, и нам нужно будет создать переменную для каждого значения, которое находится внутри объекта или массива. Вместо этого мы можем создать несколько переменных, и структурировать их в одну строку следующим образом:
const firstName, age > = person;
Здесь мы создаем переменные с именами firstName , age и берем их значения из объекта person . Иными словами мы берем свойства firstName , age и помещаем их в две новые переменные, которые будут видны родительскому блоку (или window).
Также, есть возможность переименовать переменную, если она вам не нравится или имеет непонятное имя, важно что бы имя было не занято в твоей области видимости. Например:
const firstName: name, age > = person;
Таким образом, мы извлекли значение с firstName или person.firstName и дали ему новое имя name .
Деструктуризация так же работает на массивах. Например, если тебе нужены только первый и второй элементы:
const a = [1, 2, 3, 4, 5]; const [first, second] = a; console.log(first, second); // 1, 2
Еще один вариант как выбрать нужные элементы из массива:
const [first, second, , , fifth] = a; console.log(first, second, fifth); // 1, 2, 5
Этот синтаксис создает 3 новые переменные, получая элементы с индексами 0, 1, 4 из массива a :
Расширенные литералов объекта
Более простой синтаксис
Стал более простым синтаксис для переменных, если они имеют одинаковые имена. Вместо этого:
const name = "Alex"; const x = name: name, >;
можно писать так:
const name = "Alex"; const x = name, >;
Прототип может быть указан таким образом:
const person = name: "Alex" >; const developer = __proto__: person, >;
const person = name: "Alex", say: () => "Hello " >; const developer = __proto__: person, say() return super.say() + this.name; >, >; developer.say(); // Hello Alex
Имя свойства объекта может быть указано динамически.
const x = ["a" + "_" + "b"]: "z", >; x.a_b; // z
ES5 еще в 2009 году представил цикл forEach() . Единственное, что в этом цикле нет никакого способа сделать разрыв break , как в цикле for .
ES2015 представил цикл for-of , который сочетает в себе краткость forEach с возможностью break разрыва:
// перебирает значения массива и выводит в консоль for (const v of ["a", "b", "c"]) console.log(v); > // получает индекс и значение, используя `entries()` for (const [i, v] of ["a", "b", "c"].entries()) console.log(i, v); if (i === 0) break; > >
entries() это метод который возвращает iterator позволяя пройтись по всем его ключам/значениям. В нашем случает мы используем деструктуризацию массива где i — это индекс элемента в массиве, а v — это его значение.
Map и Set (и их соответствующие сборщики мусора WeakMap и WeakSet) являются официальными реализациями двух очень популярных структур данных. Map содержит пары ключ-значение и сохраняет порядок вставки. Любое значение может быть использовано в качестве ключа. Set позволяет сохранять уникальные значения любого типа.
Новые строковые методы
Любое строковое значение получило несколько новых методов экземпляра:
- repeat() повторяет строки указанное количество раз: ‘Ho’.repeat(3) // HoHoHo;
- codePointAt() возвращает не отрицательное целое число, которое является закодированным в UTF-16 значением кодовой точки.
Новые методы объекта
ES6 представил несколько статических методов в пространстве имен объекта:
- Object.is() метод определяет, являются ли два значения одинаковыми var isSame = Object.is(value1, value2) ;
- Object.assign() используется для поверхностного копирования всех свойств объекта в целевой объект Object.assign(target, . sources) ;
- Object.setPrototypeOf устанавливает прототип объекта Object.setPrototypeOf(obj, prototype) .
ES6 в деталях: генераторы
Мне не терпится вам всё рассказать. Сегодня мы будем обсуждать самую волшебную функциональность в ES6.
Что я имел в виду под словом «волшебную»? Во-первых, эта функциональность настолько отличается от всего того, что уже есть в JS, что поначалу может показаться колдовством. В том смысле, что она выворачивает обычное поведение языка наизнанку! Если это не магия, то я не знаю, что это.
Но не только поэтому. Возможности этой фичи по упрощению кода и устранению «ада колбеков» граничат со сверхъестественным.
Я излишне нахваливаю? Давайте углубимся, и вы сами рассудите.
Знакомьтесь, генераторы ES6
Что такое генераторы?
Начнём с рассмотрения одного генератора:
function* quips(name) < yield "привет, " + name + "!"; yield "я надеюсь, вам нравятся статьи"; if (name.startsWith("X")) < yield "как круто, что ваше имя начинается с X, " + name; > yield "увидимся!"; >
Это часть кода для говорящей кошки, возможно, самого важного вида приложений в интернете на сегодняшний день. (Давайте, нажмите на ссылку, поиграйте с кошкой. Когда вы окончательно запутаетесь, возвращайтесь сюда за объяснением.)
Выглядит как-то похоже на функцию, верно? Это называется, функция-генератор, и у неё есть много общего с обычными функциями. Но вы можете заметить два отличия уже сейчас:
- Обычные функции начинаются с function . Функции-генераторы начинаются с function* .
- Внутри функции-генератора есть ключевое слово yield с синтаксисом, похожим на return . Отличие в том, что функция (в том числе функция-генератор) может вернуть значение только один раз, но отдать значение функция-генератор может любое количество раз. Выражение yield приостанавливает выполнение генератора, так что его можно позже возобновить.
Вот, именно в этом самая большая разница между обычными функциями и функциями-генераторами. Обычные функции не могут поставить себя на паузу. Функции-генераторы могут.
Что делают генераторы
Что произойдёт, если запустить функцию-генератор quips() ?
> var iter = quips("jorendorff"); [object Generator] > iter.next() < value: "привет, jorendorff!", done: false > > iter.next() < value: "я надеюсь, вам нравятся статьи", done: false > > iter.next() < value: "увидимся!", done: false > > iter.next() < value: undefined, done: true >
Возможно, вы очень привыкли к обычным функциям и тому, как они себя ведут. Когда их вызывают, они сразу же начинают выполняться и выполняются до тех пор, пока не вернут значение или не бросят исключение. Такое поведение само собой разумеется для любого JS-программиста.
Вызов генератора выглядит так же: quips(«jorendorff») . Но после того, как вы вызовете генератор, он ещё не начнёт выполняться. Вместо этого он вернёт приостановленный объект Generator (в примере выше он под именем iter ). Вы можете считать, что объект Generator — это вызов функции, замороженный во времени. Если точнее, он заморожен прямо в самом начале функции-генератора, перед первой строчкой кода.
Каждый раз, как вы вызываете метод .next() у объекта Generator, вызов функции оттаивает и выполняется, пока не достигнет следующего выражения yield .
Вот почему в примере выше после вызовов iter.next() мы всякий раз получали новое строковое значение. Эти значения производятся выражениями yield в теле quips() .
При последнем вызове iter.next() мы, наконец, достигли конца функции-генератора, так что поле .done результата стало равно true . Добраться до конца функции — это всё равно что вернуть undefined , и именно поэтому поле .value результата равно undefined .
Похоже, сейчас самое время вернуться к странице с говорящей кошкой и как следует поиграться с кодом. Попробуйте добавить yield внутрь цикла. Что произойдёт?
Говоря техническим языком, каждый раз, когда генератор отдаёт значение, его стековый кадр: локальные переменные, аргументы, временные значения и текущая позиция точки выполнения внутри тела генератора — удаляется из стека. Однако, объект Generator хранит ссылку на этот стековый кадр (или его копию), так что последующий вызов .next() возобновит его и продолжит выполнение.
Стоит отметить, что генераторы не являются потоками выполнения. В языках с потоками различные куски кода могут выполняться одновременно, обычно приводя к состояниям гонки, недетерминированности и страстно желанному приросту производительности. Генераторы вообще на это не похожи. Когда генератор выполняется, он работает в том же потоке, что и код его вызвавший. Порядок выполнения последователен и строго определён, и нет никакой параллельности. В отличие от системных потоков, генератор останавливается только на тех местах, где в коде есть yield .
Хорошо. Теперь мы знаем, что такое, генераторы. Мы видели, как генераторы выполняются, приостанавливают и возобновляют свое выполнение. Теперь хороший вопрос: как эти странные возможности могут нам пригодиться?
Генераторы — итераторы
На прошлой неделе мы увидели, что в ES6 итераторы не просто один встроенный класс. Они — точка расширения языка. Вы можете создавать собственные итераторы, и для этого нужно лишь реализовать два метода: [Symbol.iterator]() и .next() .
Но реализация интерфейса — это всегда работа, по меньшей мере, небольшая. Взглянем, как реализация итератора выглядит на практике. В качестве примера возьмём простой итератор range , который всего-навсего считает от одного числа до другого, как в старомодном цикле for (;;) из C.
// Должно "прозвенеть" трижды for (var value of range(0, 3)) < alert("Динь! на этаже № " + value); >
Вот одно решение, с использованием класса ES6. (Если синтаксис class вам не до конца ясен, не волнуйтесь, мы разберём его в одной из будущих статей.)
class RangeIterator < constructor(start, stop) < this.value = start; this.stop = stop; > [Symbol.iterator]() < return this; > next() < var value = this.value; if (value < this.stop) < this.value++; return done: false, value: value>; > else < return done: true, value: undefined>; > > > // Возвращает новый итератор, который считает от 'start' до 'stop'. function range(start, stop) < return new RangeIterator(start, stop); >
Так реализация итератора выглядит в Java или Swift. Неплохо. Но вместе с тем и нетривиально. Есть ли ошибки в этом коде? Трудно сказать. Это выглядит совершенно непохоже на изначальный цикл for (;;) , который мы пытаемся эмулировать: протокол итераторов заставляет нас разобрать этот цикл на части.
В этом месте вы можете слегка охладеть к итераторам. Может, ими и здорово пользоваться, но вот реализовывать их трудно.
Вам, возможно, не пришло бы в голову предлагать добавить новую, пугающую и мозголомную структуру потока выполнения в язык JS просто чтобы стало легче писать итераторы. Но раз уж у нас уже есть генераторы, можем ли мы их тут применить? Давайте попробуем:
function* range(start, stop) < for (var i = start; i < stop; i++) yield i; >
Вот этот генератор из 4 строчек полностью заменяет предыдущую 23-строчную реализацию range() , включая весь класс RangeIterator целиком. Это возможно потому что генераторы — это итераторы. У всех генераторов есть встроенная реализация .next() и [Symbol.iterator]() . Всё, что вам нужно — это описать поведение цикла.
Реализация итераторов без генераторов похожа на случай, когда нужно написать длинное электронное письмо используя только пассивный залог. Когда нельзя просто сказать то, что имеется в виду, речь в итоге получается весьма запутанной. RangeIterator длинный и странный потому что он должен описывать функциональность цикла не используя синтаксис циклов. Генераторы — ответ на это.
Для чего ещё можно применить возможность генераторов вести себя как итераторы?
- Преобразование любого объекта в итерируемый. Просто напишите функцию-генератор, которая перебирает this , отдавая каждое значение по мере работы. Затем установите её объекту как метод [Symbol.iterator] .
- Упрощение функций, создающих массивы. Предположим, у вас есть функция, которая каждый раз при вызове возвращает массив, вроде такой:
// Делим одномерный массив 'icons' // на массивы длиной 'rowLength'. function splitIntoRows(icons, rowLength) < var rows = []; var nRows = Math.ceil(icons.length / rowLength); for (var i = 0; i < icons.length; i += rowLength) < rows.push(icons.slice(i, i + rowLength)); >return rows; >
Генераторы могут немного сократить этот код:
function* splitIntoRows(icons, rowLength) < var nRows = Math.ceil(icons.length / rowLength); for (var i = 0; i < icons.length; i += rowLength) < yield icons.slice(i, i + rowLength); > >
function* filter(test, iterable) < for (var item of iterable) < if (test(item)) yield item; > >
Итак, генераторы полезны? Разумеется. Это удивительно лёгкий способ реализации собственных итераторов, а итераторы — это новый стандарт для данных и циклов во всём ES6.
Но это ещё не всё, что генераторы могут делать. Может даже выясниться, что это не самое важное из того, что они делают.
Генераторы и асинхронный код
Вот такой код JS я писал раньше:
Может быть, вы встретите что-то похожее в своём коде. Асинхронные APIs обычно требуют колбеков, поэтому приходится писать очередную анонимную функцию всякий раз, когда что-то делаешь. И если у вас есть кусок кода, который делает три вещи, вместо трёх строчек кода вы видите три уровня отступов кода.
Вот ещё кое-что из того JS-кода, что я писал:
>).on('close', function ( ) < done(undefined, undefined); >).on('error', function (error) < done(error); >);
В асинхронных API используются соглашения об обработке ошибок вместо исключений. У разных API могут быть разные соглашения. В большинстве из них ошибки просто игнорируются по умолчанию. В некоторых из них игнорируется по умолчанию даже обычное успешное выполнение.
До нынешнего момента эти проблемы были необходимой платой за асинхронное программирование. Мы свыклись с тем, что асинхронный код просто не выглядит так же красиво и просто, как такой же синхронный.
Генераторы дают нам новую надежду, что это так не останется.
Q.async() — это экспериментальная попытка сделать асинхронный код похожим на синхронный при помощи генераторов и промисов. К примеру:
// Синхронный код, производящий шум function makeNoise( ) < shake(); rattle(); roll(); >// Асинхронный код, производящий шум // Возвращает объект Promise, который разрешится, // когда мы закончим шуметь function makeNoise_async( ) < return Q.async(function* ( ) < yield shake_async(); yield rattle_async(); yield roll_async(); >); >
Основное отличие в том, что в асинхронной версии нужно добавлять ключевое слово yield перед любым вызовом асинхронной функции.
Если добавить конструкции вроде if или try / catch в версию Q.async , то всё будет работать точно так же, как если бы их добавили в синхронный код. По сравнению с другими способами написания асинхронного кода этот меньше всего ощущается как изучение нового языка.
Если вы дочитали до этого места, возможно, вам понравится очень подробная статья по этой теме от Джеймса Лонга (James Long).
Итак, генераторы освещают наш путь к новой модели асинхронного программирования, которая, кажется, лучше подходит для человеческого мозга. Эта работа ещё не окончена. Среди всего прочего, может помочь синтаксис получше. Предложение асинхронных функций, работающих на промисах и генераторах и вдохновлённых похожими возможностями в C#, уже внесено в таблицу на включение в ES7.
Когда можно воспользоваться этими безумными вещами?
На сервере вы можете применять генераторы уже сегодня в io.js (или в Node с параметром командной строки —harmony ).
Из браузеров пока что генераторы поддерживают только Firefox 27+ и Chrome 39+. Чтобы применять генераторы в вебе, придётся воспользоваться Babel или Traceur и транслировать код ES6 в понятный всем браузерам ES5.
Ещё кое-что, что нельзя не упомянуть: Генераторы впервые были реализованы в JS Бренданом Айком (Brendan Eich), и его подход очень напоминал генераторы в Python, которые в свою очередь были вдохновлены Icon. Они появились в Firefox в далёком 2006. Путь к стандартизации был непростым, синтаксис и поведение за это время немного поменялись. Генераторы ES6 были реализованы как в Firefox, так и в Chrome мастером компиляции Энди Винго (Andy Wingo). Его работа спонсировалась Bloomberg.
yield;
О генераторах ещё можно многое рассказать. Мы не рассмотрели методы .throw() и .return() , необязательные аргументы .next() и синтаксис выражения yield* . Но я считаю, что эта статья уже достаточно длинная, и из неё и так можно узнать много нового. Как и генераторы, мы пока приостановимся и закончим позднее.
Но на следующей неделе давайте немного сменим тему. Мы охватили две сложные темы подряд. Разве не было бы здорово в следующий раз поговорить о функциональности ES6, которая не изменит вашу жизнь? О чем-нибудь простом и очевидно полезном? О чём-то, что вызовет у вас улыбку? В ES6 и такое есть.
В следующей статье: фича, которая прекрасно подойдёт к любому коду, который вы пишете каждый день. Присоединяйтесь на следующей неделе и мы рассмотрим шаблоны строк в деталях.
ES6, ES8, ES20201: что такое ECMAScript и чем он отличается от JavaScript
Если вы тоже путаете или вообще не понимаете, чем ECMAScript отличается от JavaScript и что это такое, то это статья для вас. С небольшим углублением в историю и на конкретных примерах мы разберем, где здесь стандарт, где спецификация, а где — реализация.
Освітній курс від laba: Delivery Manager.
Ведіть проекти до успішної доставки.1. История ECMAScript
Чтобы понять, что такое ECMAScript и чем он отличается от JavaScript, нужно немного углубиться в терминологию. Начнем издалека — это важно, потому что иначе разобраться будет еще сложнее.
Есть такая организация — Ecma International. Она создает стандарты для разных технологий. Самый простой пример стандарта — клавиатура QWERTY. Дизайн может быть любым, но расположение клавиш обычно QWERTY.
Ecma International разработала ECMA-262. Это стандарт скриптового языка.
Ефективний курс від skvot: Основи 3D-моделювання в ZBrush.
Звільніть свою творчість.В стандарте ECMA-262 описана спецификация скриптового языка общего назначения. Эта спецификация называется ECMAScript.
В спецификации ECMAScript содержатся рекомендации, сведения и правила, которые должны соблюдаться, чтобы скриптовый язык считался совместимым с ECMAScript.
И, наконец, JavaScript — это скриптовый язык, который соответствует спецификации ECMAScript. Можно даже говорить, что это диалект. Большую часть лексики и синтаксиса JS взял из ECMAScript. Он отличается от своего «родителя», при этом правила и рекомендации спецификации соблюдаются. По крайней мере, в стандарте ECMA-262 сказано, что JS в основном реализует спецификацию ECMAScript, но с некоторыми отличиями.
-
Спецификация ECMAScript расскажет вам, как создать свой скриптовый язык.
Забавный факт: JavaScript появился в 1996 году. Но стандартизировать его предложили только в 1997 году. Так появился ECMAScript. Но фактически ECMAScript сейчас — это основа JavaScript, который является лишь одной из реализаций, пусть и самой популярной.
2. ECMAScript и совместимость с браузерами
Еще один неочевидный момент. В браузерах за распознавание JS-кода отвечают JavaScript-движки — например, V8 в Chrome, SpiderMonkey в Firefox. При этом, описывая поддержку браузера, обычно говорят о совместимости с ECMAScript. Выглядит запутано, но на самом деле объяснение логичное.
ECMAScript — спецификация, которая описывает, каким может быть скриптовый язык. Но появление новой версии спецификации не добавляет автоматически все новые функции в языки, сделанные на основе этой спецификации. Это относится и к JS-движкам. Все зависит от их разработчиков.
Например, новая спецификация позволяет использовать метод Array.prototype.includes для проверки того, содержится ли аргумент в массиве. Но условные разработчики SpiderMonkey не торопятся добавлять такую возможность в свой движок. Поэтому в браузере такая возможность какое-то время не поддерживается.
Професійний курс від mate.academy: Python Вечірній.
Поглиблене вивчення Python.Отсутствовать могут даже вещи, которые добавлены в спецификацию давно. Например, тот же метод Array.prototype.includes появился еще в 2016 году. Но в Internet Explorer и Opera Mini поддержки нет и не будет.
Скриншот с сайта CaniUse
Поэтому в отношении браузеров уточняют, какую версию спецификации ECMAScript они поддерживают. На сайте CanIUSe в примечаниях указано, что под полной поддержкой подразумевается доступность не менее 95% спецификации. Частичная поддержка — это когда можно использовать не менее 10% спецификации.
Когда появляется новая версия спецификации ECMAScript, новые возможности в JS-движки добавляются постепенно (или не добавляются вообще).
3. Что такое ES
Разобрались с отличиями ECMAScript от JavaScript. Осталось раскрыть последнюю тайну — что значит аббревиатура ES с разными цифрами. И здесь тоже все максимально просто — это лишь указание на очередную редакцию стандарта.
История версий ES
Ecma International постоянно обновляет стандарт ECMA-262, в котором описана спецификация ECMAScript. Версия ES1 была выпущена в 1997 году, ES2 — в 1998 году, ES3 — в 1999 году, а ES4 так и не приняли. Первые три версии ECMAScript давно устарели. В 2009 году появилась ES5. В ней, например, появились:
- поддержка strict mode ;
- getters и setters ;
- поддержка JSON ;
- многострочные строковые литералы и многое другое.
ES5 — это последняя версия спецификации ECMAScript, которую полностью поддерживают все браузеры. Да, и даже Internet Explorer.
Експертний курс від skvot: Unreal Еngine: від інтерфейсу до запуску гри.
Запустіть свою гру з Unreal.Но в 2015 году выпустили ES6. Тогда с браузерной совместимостью возникли проблемы. Для их исправления разработали Babel — это инструмент, который преобразует код ES6 в ES5. Так фронтендеры решили вопрос с поддержкой старых браузеров.
ES6 — тоже знаковое обновление. В нем, например, были добавлены стрелочные функции, появились промисы, let и const , которые стали альтернативой var для объявления переменных, заработали функции-генераторы, которые используют yield для создания последовательности элементов.
Вы также можете встретить утверждение, что Babel преобразует код ES2015 в ES5. И здесь тоже все верно. ES2015 == ES6 . Просто в 2015 году компания Ecma International стала называть версию спецификации в соответствии с годом выпуска. И перешла на ежегодные обновления. Соответственно, ES2017 == ES8, ES2021 == ES12.
Примеры изменений
Последняя версия спецификации — ES2021. Ее выпустили в июне 2021 года. Например, в ней наконец-то улучшили операторы логического присваивания. В трех операторах (&& =, || = и ?? =) объединены логическое сравнение и присваивание. Раньше нужно было писать имя переменной дважды:
// ES2020 x && (x = y), x || (х = у), х = х ?? y
В новой версии достаточно указать имя переменной один раз:
// ES2021 x && = y; х || = у; х ?? = у;
Благодаря этому снижается повторяемость кода. По сути, у логических операторов теперь такая же интеграция присваивания, как у математических операторов — +=, -=, *=.
Еще одно усовершенствование — добавление метода String.prototype.replaceAll() . Он значительно упростил замену всех вхождений строки другим строковым значением. Раньше для этого приходилось использовать регулярные выражения.
const str = "Highload High High"; const newStr = str.replace("High", "Hi"); console.log(newStr); // "Hiload High High"
Метод replace находит и заменяет только первое вхождение. Второе и последующие значения уже не учитываются. Это можно исправить, если в качестве шаблона для замены давать не строку, а регулярное выражение:
const str = "Highload High High"; const newStr = str.replace(/High/, "Hi"); console.log(newStr); // "Hiload Hi Hi"
Метод String.prototype.replaceAll() пытается заменить все вхождения , даже если в качестве шаблона подается строка:
const str = "Highload High High"; const newStr = str.replaceAll("High", "Hi"); console.log(newStr); // "Hiload Hi Hi"
Это лишь пара простых примеров. В каждой новой версии достаточно много интересных изменений, которые делают язык более удобным для работы.
4. Что в итоге
- ECMAScript — это спецификация, которая рассказывает, как создать скриптовый язык.
- JavaScript — это скриптовый язык, который в основном соблюдает правила спецификации ECMAScript. Не без исключений, но глобально его можно считать самым популярным диалектом ECMAScript.
- ES — это просто сокращенное указание очередной врсии ECMAScript. С 2015 года они выходят каждый год.
Если хотите узнать про JS-мир еще больше, посмотрите это насыщенное ценными деталями интервью с Senior JavaScript Developer:
ES5 ES6 и т.д. что это все за ху**я?
Читая статьи про Javascript новички (да и я собственно) часто сталкиваются c аббревиатурами такими как эти:
- ES3
- ES5
- ES6
- ES7
- ES8
- ES2015
- ES2016
- ES2017
- ECMAScript 2017
- ECMAScript 2016
- ECMAScript 2015
Что это все значит?
Все это относится к описанию стандарта, который называется ECMAScript По сути, ECMAScript — это стандарт на котором базируется язык JavaScript, и часто для сокращения его обозначают как ES.
История стандартизации JavaScript
Дело в том что JavaScript был выпущен в компании NetScape Navigator в 1995 году, но его изначальное название было другим — LiveScript. Лишь через год, в попытках придать ему больший охват и популярность его переименовали в JavaScript, пытаясь сыграть на популярном в то время языке Java.
в 1996 году NetScape попросил ECMA International стандартизировать JavaScript. Это привело к появлению официального стандарта ECMAScript.
С тех самых пор термин JavaScript применяется в контексте упоминания самого языка программирования, а ECMAScript — когда говорят о стандарте языка или версии этого стандарта.
До начала 21 века было выпущено 3 версии стандарта ECMAScript и затем на целое десятилетие все затихло…
ECMAScript 3 стала наиболее популярной версией JavaScript и именно ее традиционно использовали веб-разработчики по всему миру при создании сайтов.
Для разработчиков браузеров стандарт ECMAScript по сути является руководством: какие функции и конструкции языка должны поддерживаться и выполняться браузером
За десять лет после выхода третей версии стандарта была неудачная попытка выпуска 4 версии, и лишь в 2009 году комитет смог родить ECMAScript 5 — стандарт, который предусматривал новые/современные возможности JavaScript… Но браузеры не особо быстро отреагировали, а значительная доля пользователей браузера от MicroSoft — Internet Explorer тормозило внедрение и использование новых стандартов языка.
Новый JavaScript
Все стало меняться в 2012 году, разработчики стали в большинстве своем отказываться поддерживать Internet Explorer и писать код с учетом последних стандартов.
Одновременно, ECMA International подхватила активность, пересмотрев свой подход к стандартизации языка.
В результате в 2015 году был «рожден» новый стандарт получивший сокращенное название ES и согласно исторической последовательности версии присвоили 6 — в итоге получилось ES6.
Новые возможности ES6
- Новое в ES6: let и const
- Новое в ES6: строковые литералы
Также значительным прорывом стало то что комитет принял решение о ежегодном пересмотре и выпуске стандарта, в результате начиная с 2015 года мы каждый год получаем новый стандарт ECMAScript.
Для лучшего понимая исторической последовательности развития JavaScript и его стандартов смотрите табличку ниже:
Даты релиза различных версий Javascript
Издание Официальное название Дата публикации ES8 ES2017 июнь 2017 ES7 ES2016 июнь 2016 ES6 ES2015 июнь 2015 ES5.1 ES5.1 июнь 2011 ES5 ES5 декабрь 2009 ES4 ES4 отменен ES3 ES3 декабрь 1999 ES2 ES2 июнь 1998 ES1 ES1 июнь 1997 На сегодняшний день наибольшей поддержкой в браузерах обладает версия ES6 (ECMAScript 2015) однако наличие современного инструментария для разработки (транспайлеры JS) позволяет разработчикам писать код на той версии которая им больше подходит.
Как определить поддержку стандартов браузерами
C выход ES6 и последующих версий, остро встал вопрос, а будет ли новый стандарт и сами новые возможности языка работать в браузере, ведь они должны поддерживаться браузером, чтобы написанный нами современный код на JavaScript отработал в нем.
На момент выхода стандарта естественно что браузеры не поспевали за ним, но на сегодняшний день все обстоит намного лучше, ну кроме конечно Internet Explore =(
Для того чтобы понять насколько браузеры поддерживают возможности например ES6 достаточно зайти на сайт и посмотреть http://kangax.github.io/compat-table/es6/
Как не сложно заметить, на сегодня ES6 достаточно хорошо уже адаптирован браузерами и широко ими поддерживается, однако разработчику уже во всю используют не только ES6 но и ES7 и в принципе любят юзать те возможности которые выкатываются со стандартами…
Но чтобы код работал в любом браузере в независимости от стандарта его написания на заре появления ES6 возник Babel, без которого по сути сейчас не обходится работа любого frontend-разработчика.
Что такое Babel и зачем он?
Babel это транспайлер, специальный инструмент разработчика, который преобразует код JavaScript написаный более современным стандартом, в код более старого стандарта, который поддерживается большинством браузеров.
Казалось бы, ES6 уже поддерживается большинством браузеров в 2020 году, кому еще нужен Babel? Но в текущее время современный код с асинхронностью и прочим уже вышел за рамки ES6 и использует более новые версии стандарта. Плюс с приходом JS фреймворков и их активным внедрением, для их понимания браузером (например того же React) Babel стал необходим еще больше, т.к. напрямую код React в браузере не отработает, ввиду чего его нужно преобразовать при помощи Babel.
Ну и кто более глубже хочет погрузится в понимание современного JS может посмотреть видео ниже: