Что нового в es6
Обзор базовых возможностей ES6
JavaScript сильно изменился за последние годы. Вот 12 новых возможностей, которые можно начать использовать уже сегодня!
История
Новые добавления в язык называются ECMAScript 6. Или ES6 или ES2015+.
С момента появления в 1995, JavaScript развивался медленно. Новые возможности добавлялись каждые несколько лет. ECMAScript появился в 1997, его целью было направить развитие JavaScript в нужное русло. Выходили новые версии – ES3, ES5, ES6 и так далее.
Как видите, между версиями ES3, ES5 и ES6 есть пропуски длиной в 10 и 6 лет. Новая модель – делать маленькие изменения каждый год. Вместо того, чтобы накопить огромное количество изменений и выпустить их все за раз, как это было с ES6.
Browsers Support
Все современные браузеры и среды исполнения уже поддерживают ES6!
Chrome, MS Edge, Firefox, Safari, Node и многие другие системы имеют встроенную поддержку большинства возможностей JavaScript ES6. Так что, все из этого пособия можно использовать прямо сейчас.
Главные возможности ES6
Все сниппеты можно вставлять в консоль браузера и запускать.
Block scope variables
Проблема var в том, что переменная «протекает» в другие блоки кода, такие как циклы for или блоки условий if :
ES6 спешит на помощь:
Изменив var на let мы откорректировали поведение. Если блок if не вызывается, то переменная x не переопределяется.
IIFE (immediately invoked function expression)
Давайте сначала рассмотрим пример:
Как видите, private протекает наружу. Нужно использовать IIFE (immediately-invoked function expression):
В ES6 не нужно использовать IIFE, достаточно использовать блоки и let :
Const
Можно также использовать const если переменная не должна изменяться.
Template Literals
Не нужно больше делать вложенную конкатенацию, можно использовать шаблоны. Посмотрите:
Multi-line strings
Не нужно больше конкатенировать строки с + \n :
В ES6 можно снова использовать бэктики:
Оба блока кода генерируют одинаковый результат
Destructuring Assignment
ES6 destructing – полезная и лаконичная штука. Посмотрите на примеры:
Получение элемента из массива
Обмен значениями
Деструктуризация нескольких возвращаемых значений
В строке 3 можно вернуть в виде массива:
но вызывающему коду придется знать о порядке данных.
С ES6 вызывающий выбирает только нужные данные (строка 6):
Заметка: В строке 3 содержатся другие возможности ES6. Можно сократить < left: left >до < left >. Смотрите, насколько это лаконичнее по сравнению с версией ES5. Круто же?
Деструктуризация и сопоставление параметров
То же самое (но короче):
Глубокое сопоставление
То же самое (но короче):
Это также называют деструктуризацией объекта (object destructing).
Как видите, деструктуризация может быть очень полезной и может подталкивать к улучшению стиля кодирования.
Классы и объекты
Каждый объект в JavaScript имеет прототип, который является другим объектом. Все объекты в JavaScript наследуют методы и свойства от своего прототипа.
В ES5 объектно-ориентированное программирование достигалось с помощью функций-конструкторов. Они создавали объекты следующим образом:
Оба стиля (ES5/6) дают одинаковый результат.
Наследование
В ES5 придется немного поработать с прототипным наследованием.
Не будем вдаваться в детали, но заметьте несколько деталей:
Посмотрите, насколько лучше выглядит код на ES6 по сравнению с ES5. И они делают одно и то же! Win!
Нативные промисы
Переходим от callback hell к промисам (promises)
Одна функция принимает callback чтобы запустить его после завершения. Нам нужно запустить ее дважды, одну за другой. Поэтому приходится вызывать printAfterTimeout во второй раз в коллбеке.
Все становится совсем плохо когда нужно добавить третий или четвертый коллбек. Давайте посмотрим, что можно сделать с промисами:
С помощью then можно обойтись без вложенных функций.
Стрелочные функции
В ES5 обычные определения функций не исчезли, но был добавлен новый формат – стрелочные функции.
В ES5 есть проблемы с this :
For…of
От for переходим к forEach а потом к for. of :
ES6 for…of позволяет использовать итераторы
Параметры по умолчанию
От проверки параметров переходим к параметрам по умолчанию. Вы делали что-нибудь такое раньше?
Скорее всего да. Это распространенный паттерн проверки наличия значения переменной. Но тут есть некоторые проблемы:
Если параметр по умолчанию это булева переменная или если задать значение 0, то ничего не получится. Почему? Расскажу после этого примера с ES6 😉
В ES6 все получается лучше с меньшим количеством кода:
С проверкой на undefined все работает как нужно.
Rest-параметры
От аргументов к rest-параметрам и операции spread.
В ES5 работать с переменным количеством аргументов неудобно.
Операция Spread
В ES6 используем spread:
Мы также перешли от concat к spread’у:
Заключение
JavaScript сильно изменился. Эта статья покрывает только базовые возможности, о которых должен знать каждый разработчик.
JavaScript ES6: пишем меньше — делаем больше
Стандарт ES6 принёс в мир JavaScript-разработки новые синтаксические конструкции и замечательные возможности, которые, кроме прочего, улучшают читаемость текстов программ, позволяют сделать их лаконичнее и выразительнее. Всё это позволяет разработчику решать те же задачи, что и раньше, написав меньше кода. «Пишем меньше — делаем больше», — это идея, которая вдохновила автора материала, перевод которого мы сегодня публикуем, на исследование возможностей ES6. В частности, здесь он сравнивает ES5 и ES6 и рассматривает варианты практического использования новых конструкций.
Ключевые слова const и let
В вышеприведённом примере значение, записанное в константу, изменить нельзя. При попытке записи в неё нового значения мы столкнёмся с сообщением об ошибке.
Стрелочные функции
Стрелочные функции — просто замечательная возможность. Они улучшают читаемость и благотворно влияют на структуру кода, да и попросту делают программы выглядящими современнее. Вот как пользуются функциями в ES5.
А вот — современный вариант.
Шаблонные литералы
Шаблонные литералы, или шаблонные строки — возможность очень интересная. Теперь не нужно использовать оператор + для конкатенации строк, или в тех случаях, когда при сборке строк используются переменные. Вот как это выглядело раньше.
Вот что у нас есть теперь.
По-новому получается гораздо проще. Перед нами — весьма значительное отличие между старым синтаксисом и ES6. Новые возможности ES6 делают работу со строками более организованной и способствуют улучшению структуры соответствующих фрагментов кода.
Параметры функций, задаваемые по умолчанию
Рассмотрим пример, в котором параметры, задаваемые по умолчанию, не используются.
Деструктурирование массивов и объектов
Деструктурирование упрощает операцию присвоения отдельных значений, хранящихся в массивах или объектах, новым переменным. Вот как подобное можно было сделать раньше.
Вот как то же самое делается средствами ES6.
Пользуясь ES5, мы должны были присваивать каждое отдельно взятое значение каждой переменной. Благодаря возможностям ES6 для того, чтобы извлечь отдельные значения объекта, имена переменных достаточно поместить в фигурные скобки.
В случае с массивами используется похожий синтаксис. Только фигурные скобки надо заменить скобками квадратными.
Инструкции import и export
Использование инструкций import и export в JS-приложениях расширяет их возможности. Они позволяют программисту создавать самостоятельные компоненты, подходящие для повторного использования.
Если вы знакомы с любым MVC-фреймворком для JavaScript, вы уже знаете, что они практически всегда используют инструкции import и export для работы с компонентами. Как же всё это устроено? На самом деле — очень просто. Инструкция export позволяет экспортировать модуль, который может быть использован в других JavaScript-компонентах. Инструкция import используется для импорта подобных модулей.
Если нужно импортировать несколько модулей, их можно перечислить в фигурных скобках.
Промисы
Промисы — это одна из новых возможностей ES6. Это — подход к написанию асинхронного кода. Его можно использовать когда, например, нужно загрузить данные из некоего API, или когда у нас имеется функция, на выполнение которой нужно некоторое время. Промисы упрощают решение подобных задач. Вот как с ними работать.
Здесь показано, что попадёт в консоль при попытке логирования промиса.
В данном случае вызов console.log приводит к выводу в консоль массива.
Синтаксис оставшихся параметров и оператор расширения
Синтаксис оставшихся параметров используется для представления неограниченного множества аргументов в виде массива. Вот несколько примеров его использования.
Оператор расширения похож на использование синтаксиса оставшихся параметров, но он позволяет, как показано в следующем примере, работать с массивами, извлекая их значения, которые иначе пришлось бы извлекать, пользуясь циклом for или какими-то другими методами.
Классы
Классы — это одно из базовых понятий объектно-ориентированного программирования. Они позволяют улучшить структуру кода и сделать его безопаснее.
Здесь можно почитать подробности о классах в JavaScript.
Итоги
Надеемся, этот материал помог вам открыть среди возможностей ES6 что-то новое, полезное в вашей практической работе. Кстати, здесь описаны далеко не все новшества ES6. Ознакомиться с гораздо более обширным их списком можно здесь. Кроме того, если вас интересует тема новых возможностей JavaScript, вот, вот и вот — некоторые наши публикации на эту тему.
Уважаемые читатели! Можете ли вы привести примеры из практики, иллюстрирующие идею, в соответствии с которой применение новых возможностей ES6 позволяет решать те же задачи, что и раньше, с помощью меньшего объёма кода, который, к тому же, оказывается проще, понятнее и читабельнее?
ECMAScript 6
Границы моего языка олицетворяют границы моего мира.
— Людвиг Витгенштейн
Последние несколько месяцев я пишу только ECMAScript 6 код, воспользовавшись трансформацией [1] в поддерживаемые в настоящее время версии JavaScript.
ECMAScript 6, далее ES6 и ранее ES.next, является последней версией спецификации. По состоянию на август 2014 новые возможности не обсуждаются, но детали и крайние случаи до сих пор уточняются. Ожидается, что стандарт будет завершен и опубликован в середине 2015 года.
Принятие ES6 одновременно привело к повышению производительности (что делает мой код более лаконичным) и ликвидации целого класса ошибок путем устранения распространённых подводных камней JavaScript.
Это должно быть достаточно очевидным для вас, если вы использовали CoffeeScript, который сосредотачивается на хороших частях JS и скрывает плохие. ES6 смог принять на столько много инноваций из CoffeeScript, что некоторые даже ставят под сомнение дальнейшее развитие последнего.
For all intents and purposes, JavaScript has merged CoffeeScript into master. I call that a victory for making things and trying them out.
Вместо того, чтобы сделать тщательный анализ новых возможностей я расскажу о наиболее интересных из них. Чтобы стимулировать разработчиков обновляться, новые языки и фреймворки должны (1) иметь убедительную историю совместимости и (2) предлагать вам достаточно большую морковку.
# Синтаксис модулей
ES6 знакомит с синтаксисом для определения модулей и объявления зависимостей. Я подчеркиваю слово синтаксис потому что ES6 не имеет отношение к фактической реализации того, как модули будут выбраны или загружены.
Это еще более укрепляет взаимодействие между различными контекстами, в которых может выполняться JavaScript.
Рассмотрим в качестве примера простую задачу написания многоразового использования CRC32 в JavaScript.
До сих пор, не существовало никаких рекомендаций о том, как на самом деле решить эту задачу. Общий подход это объявить функцию:
С оговоркой, конечно же, что она вводит единое фиксированное глобальное имя, на которое другие части кода будут должны ссылаться. И с точки зрения кода, который использует crc32 функцию, нет способа объявить зависимость. Как только функция была объявлена, она будет существовать до тех пор, пока код не будет интерпретирован.
Типичный сценарий, чтобы проиллюстрировать эти недостатки это генерация связки модулей для браузера, с помощью таких инструментов, как browserify или webpack. Они еще находятся в зачаточном состоянии, потому что они воспринимают require() как синтаксис, эффективно избавляя себя от свойственного им динамизма.
Приведенный пример не подлежит статическому анализу, поэтому если вы попытаетесь транспортировать этот код в браузер, то он сломается:
Другими словами, алгоритм упаковщика не может знать заранее, что означает woot().
ES6 ввел правильный набор ограничений, учитывая большинство существующих вариантов использования, черпая вдохновение из самых неформально-существующих специальных модульных систем, как jQuery $.
Синтаксис требует некоторого привыкания. Наиболее распространенный шаблон для определения зависимостей удивительно непрактичен.
последний считается именованным экспортом и требует синтаксис в конструкции import:
Другими словами, самая простая (и, пожалуй, наиболее желательная) форма определения модуля требует дополнительное ключевое слово default. Или в случае его отсутствия, использование при импорте.
# Деструктуризация
Одним из наиболее распространенных шаблонов, возникших в современном JavaScript коде является использование вариантных объектов.
Такая практика широко используется в новых браузерных API, например в WHATWG fetch (современная замена XMLHttpRequest):
Повсеместное принятие этой модели эффективно препятствует падению экосистемы JavaScript в логическую ловушку.
Если принять, что API принимает обычные аргументы, а не объект с параметрами, то вызов fetch превращается в задачу запоминания порядка аргументов и ввода ключевого слова null в нужное место.
Со стороны реализации, однако, это не выглядит так же красиво. Глядя на объявление функции, ее сигнатура больше не описывает входные возможности:
Обычно это сопровождается ручной установкой значений по-умолчанию локальным переменным:
И к сожалению для нас, несмотря на свою распространенность, практика использования || фактически привносит трудно выявляемые ошибки. Например, в этом случае мы не допускаем того, что opts.body может быть 0, поэтому надежный код скорее всего будет выглядеть так:
Благодаря деструктуризации мы можем сразу четко определить параметры, правильно задать значения по умолчанию и выставить их в локальной области видимости:
Собственно, значение по умолчанию можно применить и ко всему объекту с параметрами:
Вы также можете деструктурировать оператор присваивания:
Это напоминает мне о выразительности, предоставленные with, но без магии или негативных последствий.
# Новые соглашения
Некоторые части языка были полностью заменены лучшими альтернативами, что быстро станет новым стандартом того, как вы пишете JavaScript.
Я расскажу о некоторых из них.
# let/const вместо var
Вместо того, чтобы писать var x = y скорее всего вы будете писать let x = y. let позволяет объявлять переменные с блочной областью видимости:
Это особенно полезно для for или while циклов:
Используйте const, если вы хотите обеспечить неизменяемость с той же семантикой, как и let.
# строковые шаблоны вместо конкатенации
В связи с отсутствием sprintf или подобными утилитами в стандартной библиотеки JavaScript, составление строк всегда было более болезненным, чем следовало бы.
Строковые шаблоны сделали встраивание выражений в строки тривиальной операцией, также как и поддержку нескольких линий. Просто замените ‘ на `
# классы вместо прототипов
Определение класса было громоздкой операцией и требовало глубокого знания внутреннего устройства языка. Даже несмотря на то, что, польза понимания внутреннего устройства очевидна, порог входа для новичков был неоправданно высоким.
class предлагает синтаксический сахар для определения функции конструктора, методов прототипа и геттеров / сеттеров. Он также реализует прототипное наследование со встроенным синтаксисом (без дополнительных библиотек или модулей).
Я изначально был удивлен, узнав, классы не всплывают (hoisted) (объяснение тут). Поэтому вы должны думать о них, переводя в var A = function()<> в противоположность function A()<>.
# ()=> вместо function
Не только потому что (x, y) => <> короче написать, чем function (x,y) <>, но поведение this в теле функции, скорее всего, будет ссылаться на то, что вы хотите.
Так называемые функции “толстые стрелки” лексически связанны. Рассмотрим пример метода внутри класса, который запускает два таймера:
К ужасу новичков, первый таймер (с использованием function) выведет «undefined». А вот второй правильно выведет name.
# Первоклассная поддержка async I/O
Асинхронное выполнение кода сопровождало нас в течение почти всей истории языка. setTimeout, в конце концов, был введен примерно в то время, когда вышел JavaScript 1.0.
Но, пожалуй, язык не поддерживает асинхронность на самом деле. Возвращаемое значение вызовов функций, которые запланированы “выполниться в будущем” обычно равны undefined или в случае с setTimeout — Number.
Введение Promise позволило заполнить очень большую пропасть в совместимости и композиции.
С одной стороны, вы найдете API более предсказуемым. В качестве теста, рассмотрим новое fetch API. Как это работает за сигнатурой, которую мы только что описали? Вы угадали. Оно возвращает Promise.
Если Вы использовали Node.JS в прошлом, вы знаете, что есть неформальная договоренность о том, что обратные вызовы следуют сигнатуре:
Также неофициально указана идея о том, что обратные вызовы будут вызываться только один раз. И null будет значение в случае отсутствия ошибок (а не undefined или false). За исключением, возможно, это не всегда так.
# Вперед к будущему
ES6 набирает немалые обороты в экосистеме. Chrome и io.js уже добавили некоторый функционал из ES6. Много уже было написано об этом.
Но стоит отметить то, что эта популярность была во многом обусловлена наличием утилит для трансформации, а не фактической поддержкой. Отличные инструменты появились, для того чтобы включить трансформацию и эмуляцию ES6, и браузеры со временем добавили поддержку отладки кода и отлова ошибок (с помощью карт кода).
Эволюция языка и его предполагаемый функционал, опережают реализацию. Как говорилось выше, Promise — по-настоящему интересен как самостоятельный блок, который предлагает решение проблемы callback hell раз и навсегда.
Стандарт ES7 предлагает сделать это путем введения возможности ожидания (async) объекта Promise:
Хотя эта спецификация обсуждается уже давно, тот же инструмент, который компилирует ES6 в ES5 уже реализовал это.
Предстоит еще много работы для того, чтобы убедиться, что процесс принятия нового синтаксиса языка и API становится еще более лишенным странностей для тех, кто только приступает к работе.
Но одно можно сказать точно: мы должны принять это будущее.
Сноски:
1. ^ я использую слово “трасформация” в статье, чтобы объяснить компиляцию исходного кода в исходный код в JavaScript. Но значение этого термина технически спорно.
ES6 по-человечески
От переводчика:
Предлагаю вашему вниманию перевод краткого (действительно краткого) руководства по ES6. В нём можно ознакомиться с основными понятиями стандарта.
Оригинальный текст в некоторых случаях был дополнен или заменён на более подходящий источник. Например, часть определения ключевого слова const является переводом документации с MDN.
Чтобы лучше разобраться в некоторых концепциях (для выполнения качественного перевода) использовалось описание стандарта на сайте MDN, руководство «You Don’t Know JS: ES6 & Beyond» и учебник Ильи Кантора.
Содержание
1. let, const и блочная область видимости
О чём стоит помнить:
2. Стрелочные функции
Заметим, что в примере выше, тело функции представляет собой краткую запись, в которой не требуется явного указания на то, что мы хотим вернуть результат.
А вот пример с использованием блока из фигурных скобок:
Это ещё не всё.
Стрелочные функции не просто делают код короче. Они тесно связаны с ключевым словом this и привязкой контекста.
В ECMAScript 3/5 это поведение стало возможным изменить, присвоив значение this другой переменной.
Как сказано выше, внутри стрелочных функций значение this то же самое, что и снаружи, поэтому следующий код работает так, как от него и ожидается:
3. Параметры по умолчанию
ES6 позволяет установить параметры по умолчанию при объявлении функции. Вот простой пример:
4. Spread / Rest оператор
. оператор называют как spread или rest, в зависимости от того, как и где он используется.
При использовании в любом итерируемом объекте (iterable), данный оператор «разбивает» («spread») его на индивидуальные элементы:
5. Расширение возможностей литералов объекта
ES6 позволяет объявить литералы объекта с помощью короткого синтаксиса для инициализации свойств из переменных и определения функциональных методов. Также, стандарт обеспечивает возможность вычисления свойств непосредственно в литерале объекта.
6. Восьмеричный и двоичный литералы
В ES6 появилась новая поддержка для восьмеричных и двоичных литералов.
Добавление к началу числа 0o или 0O преобразует его в восьмеричную систему счисления (аналогично, 0b или 0B преобразует в двоичную систему счисления). Посмотрим на следующий код:
7. Деструктуризация массивов и объектов
Деструктуризация помогает избежать использования вспомогательных переменных при взаимодействии с объектами и массивами.
8. Ключевое слово super для объектов
ES6 позволяет использовать метод super в (безклассовых) объектах с прототипами. Вот простой пример:
9. Строковые шаблоны и разделители
ES6 предоставяляет более простой способ вставки значения переменной или результата выражения (т.н. «интерполяцию»), которые рассчитываются автоматически.
10. for. of против for. in
11. Map и WeakMap
Классический объект состоит из ключей (всегда в строковом виде) и значений, тогда как в Map для ключа и значения можно использовать любое значение (и объекты, и примитивы). Посмотрим на этот код:
WeakMap
12. Set и WeakSet
Объекты Set это коллекции уникальных значений. Дублированные значения игнорируются, т.к. коллекция должна содержать только уникальные значения. Значения могут быть примитивами или ссылками на объекты.
WeakSet
13. Классы в ES6
В ES6 представили новый синтаксис для классов. Здесь стоит отметить, что класс ES6 не представляет собой новую объектно-ориентированную модель наследования. Это просто синтаксический сахар для существующего в JavaScript прототипного наследования.
Класс в ES6 представляет собой просто новый синтаксис для работы с прототипами и функциями-конструкторами, которые мы привыкли использовать в ES5.
extends и super в классах
Посмотрим на следующий код:
В ES6 ключевое слово extends позволяет классу-потомку наследовать от родительского класса. Важно отметить, что конструктор класса-потомка должен вызывать super().
О чём стоит помнить:
14. Тип данных Symbol
Symbol это уникальный и неизменяемый тип данных, представленный в ES6. Целью Symbol является создание уникального идентификатора, к которому нельзя получить доступ.
Вот как можно создать Symbol :
Заметим, что использовать new вместе с Symbol(…) нельзя.
Если Symbol используется как свойство/ключ объекта, он сохраняется таким специальным образом, что свойство не будет показано при нормальном перечислении свойств объекта.
Чтобы извлечь символьные свойства объекта, нужно использовать Object.getOwnPropertySymbols(o)
15. Итераторы
Посмотрим на массив, который является перебираемым (iterable), и на итератор, который есть у массива для обработки его значений:
Заметим, что можно написать собственный итератор через определение obj[Symbol.iterator]() с описанием объекта.
Подробнее про итераторы:
На сайте MDN
16. Генераторы
Функции-генераторы представляют собой новую особенность ES6, которая позволяет функции создавать много значений в течение некоторого периода времени, возвращая объект (называемый генератором), который может быть итерирован для выброса значений из функции по одному за раз.
Каждый раз при вызове yield возвращённое значение становится следующим значением в последовательности.
Также заметим, что генераторы вычисляют свои возвращённые значения по запросу, что позволяет им эффективно представлять последовательности, затратные с точки зрения вычислений, или даже бесконечные последовательности.
17. Промисы
В ES6 появилась встроенная поддержка промисов. Промис это объект, который ждёт выполнения асинхронной операции, после которого (т.е. после выполнения) промис принимает одно из двух состояний: fulfilled (resolved, успешное выполнение) или rejected (выполнено с ошибкой).
При возвращении промиса, успешно обработанное значение промиса пройдёт к следующему коллбэку, для того, чтобы эффективно соединить их вместе.
Эта простая техника помогает избежать ада с коллбэками («callback hell»).