Моя серия заметок ES6 in Depth, состоящая из 24 записей, описывает большинство синтаксических изменений и нововведений в ES6. В этой публикации я подведу итог всего изложенного в предыдущих статьях, чтобы дать возможность посмотреть еще раз на всё вместе.



Содержание

  • Введение
  • Инструментарий
  • Assignment Destructing
  • Spread Operator и Rest Parameters
  • Стрелочные функции
  • Шаблонные строки
  • Литералы объектов
  • Классы
  • Let и Const
  • Символы
  • Итераторы
  • Генераторы
  • Промисы
  • Maps
  • WeakMaps
  • Sets
  • WeakSets
  • Прокси
  • Reflection
  • Number
  • Math
  • Array
  • Object
  • Строки и Unicode
  • Модули

Часть первая: здесь.

Символы

  • Новый примитивный тип данных в ES6.
  • Можно создавать собственные символы: var symbol = Symbol()
  • Можно добавить описание для нужд отладки Symbol('ponyfoo')
  • Символы неизменяемы и уникальны: Symbol() , Symbol() , Symbol('foo') и Symbol('foo') – все разные.
  • Тип символов – symbol , так что typeof Symbol() === 'symbol' .
  • Можно создавать глобальные символы при помощи Symbol.for(key) .

  • Если символ с этим key существует, вызов его вернет.
  • Иначе будет создан новый символ с key в качестве описания.
  • Symbol.keyFor(symbol) – это обратная функция, принимающая symbol и возвращающая его key .
  • Глобальные символы глобальны, насколько это возможно, то есть абсолютно. Для доступа к символам используется единый глобальный регистр:
  • контекст window ;
  • контекст eval ;
  • контекст <iframe> Symbol.for('foo') === iframe.contentWindow.Symbol.for('foo') .

  • Также есть «широко известные» символы:
  • не в глобальном регистре, доступный через Symbol[name], например Symbol.iterator ;
  • глобальный, в смысле Symbol.iterator === iframe.contentWindow.Symbol.iterator ;
  • используемый спецификациями для определения протоколов, таких как iterable protocol через Symbol.iterator ;
  • они не являются «широко известными» в общепринятом смысле.
  • Итерировать по свойствам символов можно, это сложно и абсолютно не приватно.
  • Символы скрыты для всех reflection-методов, предшествующих ES6.
  • Символы доступны через Object.getOwnPropertySymbols .
  • Вы не будете через них спотыкаться, но обязательно их найдете при активном поиске.
  • Прочитайте ES6 Symbols in Depth.


Итераторы

  • Итератор и iterable protocol определяют способ, с помощью которого будет происходить итерация по любому объекту, не только массивоподобному или массиву.
  • «Широко известные» Symbol используются для назначения итератора любому объекту.
  • var foo = { [Symbol.iterator]: iterable} , или foo[Symbol.iterator] = iterable .
  • iterable является методом, возвращающим объект итератора, у которого есть метод next .
  • Метод next возвращает объект с двумя свойствами: value and done .
  • Свойство value указывает на текущее значение в последовательности, по которой происходит итерация.
  • Свойство done указывает на наличие оставшихся элементов для итерации.
  • Объекты, имеющие значение [Symbol.iterator] , доступны для итерации по ним, так как являются подписанными на iterable protocol.
  • Некоторые встроенные штуки вроде Array , String или arguments NodeList в браузерах) по умолчанию доступны для итерации в ES6.
  • Доступные для итерации объекты могут быть перебраны циклом for..of , так же как и for (let el of document.querySelectorAll('a')) .
  • Доступные для итерации объекты могут быть созданы при помощи spread operator, например [...document.querySelectorAll('a')] .
  • Также можно использовать Array.from(document.querySelectorAll('a')) с целью создания массива из доступной для итерации последовательности.
  • Итераторы «ленивы», и те из них, которые создают бесконечный цикл, всё еще могут быть использованы в валидных программах.
  • Будьте осторожны и не пытайтесь создать бесконечную последовательность при помощи… или Array.from , потому что это приведет к созданию бесконечного цикла.
  • Прочитайте ES6 Iterators in Depth.


Генераторы

  • Функции-генераторы – это особый вид итератора, который может быть объявлен при помощи синтаксиса function* generator () {}.
  • Функции-генераторы используют ключевое слово yield , чтобы возвращать элемент в последовательности.
  • Функции-генераторы также могут использовать yield* для отсылки на другую функцию-генератор или любой доступный для итерации объект.
  • Функции-генераторы возвращают объект генератора, который придерживается протоколов итератора и iterable.
  • Дано g = generator(), g придерживается iterable-протокола, потому что g[Symbol.iterator] является методом.
  • Дано g = generator(), g придерживается протокола итератора, потому что g.next является методом.
  • Итератор для объекта генератора g – это сам генератор: g[Symbol.iterator]() === g .
  • Можно получать значения при помощи Array.from(g) , [...g], for (let item of g ) или просто вызвав g.next() .
  • Выполнение функции-генератора приостанавливается с запоминанием при этом последнего значения в четырех различных случаях:
  • выражение yield возвращает следующее значение в очереди;
  • выражение return возвращает последнее значение в очереди;
  • выражение throw полностью останавливает работу генератора;
  • достижение конца функции-генератора сигнализирует { done: true } .
  • Как только очередь g закончилась, g.next() просто возвращает { done: true} , и больше ничего не происходит.
  • Очень просто писать асинхронный код в синхронном стиле.
  • Используйте пользовательскую функцию-генератор.
  • Пользовательский код останавливается на время выполнения асинхронных операций.
  • Вызов g.next() возобновляет выполнение пользовательского кода.
  • Прочитайте ES6 Generators in Depth.


Промисы

  • Следуют спецификации Promises/A+, уже широко применяемой на практике задолго до того, как ES6 был стандартизирован (например bluebird).
  • Промисы имеют древовидное поведение. Добавляйте ветви p.then(handler) и p.catch(handler) .
  • Создавайте новые промисы p при помощи new Promise((resolve, reject) => { /* resolver */ }) .
  • Callback resolve(value) выполнит промис с указанным value .
  • Callback reject(reason) отклонит выполнение промиса с ошибкой reason.
  • Эти методы можно вызывать асинхронно, блокируя более глубокие ветви в дереве промисов.
  • Каждый вызов p.then и p.catch создает еще один промис, который блокируется на время выполнения p .
  • Промисы создаются в состоянии ожидания и могут быть разрешены, будучи выполненными или отклоненными.
  • Промис можно разрешить только один раз. Он становится разрешенным и разблокирует более глубоко вложенные ветви.
  • Можно прикрепить сколько угодно промисов к любому необходимому количеству ветвей.
  • Каждая ветвь выполнит свой обработчик . then или .catch , но никогда не выполняет оба обработчика.
  • Callback .then может выполнить преобразование результата предыдущей ветви, вернув значение.
  • Callback .then может блокировать другой промис, вернув его.
  • p.catch(fn).catch(fn) не станет делать то, что вы хотите. Разве что вы хотите перехватывать ошибки в обработчике ошибок.
  • Promise.resolve(value) создает промис, разрешенный с указанным value .
  • Promise.reject(reason) создает промис, отклоненный с указанным reason .
  • Promise.all(...promises) создает промис, который разрешается, когда все промисы ...promises выполнены или один из них отклонен.
  • Promise.race(...promises) создает промис, который разрешается, как только один из промисов ...promises будет выполнен.
  • Используйте Promisees – песочницу для визуализации промисов, чтобы понять механизм их работы еще лучше.
  • Прочитайте ES6 Promises in Depth.


Maps

  • Замена распространенного паттерна создания hash-map при помощи простых объектов JavaScript.
  • Помогает избежать проблем с безопасностью при работе с пользовательскими ключами.
  • Можно использовать произвольные значения в качестве ключей, даже DOM-элементы или функции.
  • Map придерживается iterable-протокола.
  • Создавайте map при помощи new Map() .
  • Инициализируйте map при помощи iterable вида [[key1, value1], [key2, value2]] в new Map(iterable) .
  • Используйте map.set(key, value) для добавления элементов.
  • Используйте map.get(key) для получения элементов.
  • Проверяйте наличие key при помощи map.has(key) .
  • Удаляйте элементы при помощи map.delete(key) .
  • Итерировать по map можно при помощи for (let [key, value] of map) , the spread operator, Array.from , и т. д.
  • Прочитайте ES6 Maps in Depth.


WeakMaps

  • Похоже на Map , но не то же.
  • WeakMap не придерживается iterable протокола, так что у него нет таких методов перечисления, как .forEach , .clear и других, присутствующих в Map .
  • Ключи WeakMap должны быть ссылками. Нельзя использовать данные типа symbol, number или string в качестве ключа.
  • Элементы WeakMap с key , являющимся единственной ссылкой на переменную, подбираются сборщиком мусора.
  • Из предыдущего пункта следует, что WeakMap – отличный способ хранения метаданных об объектах, которые всё еще используются.
  • Вы избегаете утечек памяти без ручного учета ссылок – думайте о WeakMap как об IDisposable в .NET.
  • Прочитайте ES6 WeakMaps in Depth.


Sets

  • Похоже на Map , но не то же.
  • В Set нет ключей, только значения.
  • set.set(value) выглядит не очень, так что вместо него есть set.add(value) .
  • В Set не может быть двух одинаковых значений, потому что значения Set – это его ключи.
  • Прочитайте ES6 Sets in Depth.


WeakSets

  • Нечто среднее между Set и WeakMap.
  • WeakSet – это set, по которому нельзя итерировать и у которого нет методов перечисления.
  • Ключи WeakSet должны быть ссылками.
  • WeakSet может быть использован как таблица метаданных, показывающая, является ли ссылка активной.
  • Прочитайте ES6 WeakSets in Depth.


Прокси

  • Создать прокси можно при помощи new Proxy(target, handler) , где target – это любой объект, а handler – это настройки.
  • По умолчанию объект proxy ведет себя как ссылка на объект target .
  • Обработчики определяют способ осуществления доступа к объекту target при помощи стандартной семантики доступа к свойствам объектов.
  • Вы передаете ссылки в proxy и получаете полный контроль над способом общения с target .
  • Обработчики (handlers) также известны как ловушки (traps) – эти термины взаимозаменяемы.
  • Вы можете создавать отзываемые прокси при помощи Proxy.revocable(target, handler) .
  • Этот метод вернет объект со свойствами proxy и revoke .
  • Для удобства можно писать так: var {proxy, revoke} = Proxy.revocable(target, handler) .
  • Можно настраивать proxy точно так же, как и при помощи new Proxy(target, handler) .
  • После вызова revoke() proxy будет бросать исключение при любой операции, что очень удобно, когда вы не можете доверять пользователям вашего кода.
  • get – ловит proxy.prop и proxy['prop'] .
  • set – ловит proxy.prop = value и proxy['prop'] = value .
  • has – ловит оператор in .
  • deleteProperty – ловит оператор delete operator .
  • defineProperty – ловит Object.defineProperty и декларативные альтернативы.
  • enumerate – ловит циклы for..in .
  • ownKeys – ловит Object.keys и подобные методы.
  • apply – ловит вызовы функций.
  • construct – ловит использование оператора new .
  • getPrototypeOf – ловит внутренние вызовы [[GetPrototypeOf]] .
  • setPrototypeOf – ловит вызовы Object.setPrototypeOf.
  • isExtensible – ловит вызовы Object.isExtensible .
  • preventExtensions – ловит вызовы Object.preventExtensions .
  • getOwnPropertyDescriptor – ловит вызовы Object.getOwnPropertyDescriptor .
  • Прочитайте ES6 Proxies in Depth.
  • Прочитайте ES6 Proxy Traps in Depth.
  • Прочитайте More ES6 Proxy Traps in Depth.


Reflection
  • Reflection – новый статический встроенный элемент (вроде Math ) в ES6.
  • Методы Reflection являются чувствительными, например, Reflect.defineProperty возвращает boolean вместо того, чтобы бросить ошибку.
  • Существует метод Reflection для каждого обработчика прокси, он показывает поведение обработчика по умолчанию.
  • Забегая наперед, можно сказать, что новые методы reflection будут заменены в пространстве имен Reflection в том же ключе, что и Object.keys.
  • Прочитайте ES6 Reflection in Depth.


Number

  • Используйте префикс 0b для бинарных литералов и 0o – для восьмеричных чисел.
  • Number.isNaN и Number.isFinite работают так же, как и соответствующие глобальные методы, за исключением того, что не приводят вводные данные к типу Number.
  • Number.parseInt и Number.parseFloat абсолютно идентичны соответствующим глобальным методам.
  • Number.isInteger проверят соответствие вводных данных типу Number – значение не должно иметь десятичной части.
  • Number.EPSILON помогает найти незначительные различия между двумя числами, например, между 0.1 + 0.2 и 0.3 .
  • Number.MAX_SAFE_INTEGER – самое большое целое число, которое может быть безопасно и точно представлено в JavaScript.
  • Number.MIN_SAFE_INTEGER – самое малое целое число, которое может быть безопасно и точно представлено в JavaScript.
  • Number.isSafeInteger проверяет, может ли число быть представлено безопасно и точно.
  • Прочитайте ES6 Number Improvements in Depth.


Math

  • Math.sign – знак числа.
  • Math.trunc – целая часть числа.
  • Math.cbrt – кубический корень значения ??value .
  • Math.expm1 – экспонента значения — 1 , или evalue – 1.
  • Math.log1p – натуральный логарифм значения + 1, или ln(value + 1) .
  • Math.log10 – десятичный логарифм значения, или <sub>log10</sub> (value) .
  • Math.log2 – двоичный логарифм значения, или <sub>log2</sub> (value)
  • Math.sinh – гиперболический синус числа.
  • Math.cosh – гиперболический косинус числа.
  • Math.tanh – гиперболический тангенс числа.
  • Math.asinh – гиперболический арксинус числа.
  • Math.acosh – гиперболический арккосинус числа.
  • Math.atanh – гиперболический арктангенс числа.
  • Math.hypot – квадратный корень суммы квадратов.
  • Math.clz32 – количество ведущих нулевых битов в 32-битном представлении числа.
  • Math.imul – C-like 32-битное умножение.
  • Math.fround – округление числа до одного знака после запятой.
  • Прочитайте ES6 Math Additions in Depth.

Array

  • Array.from – создание экземпляра массива из массивоподобных объектов вроде arguments или iterables.
  • Array.of – похоже на new Array(...items) , но без частных случаев.
  • Array.prototype.copyWithin – копирует последовательность элементов массива в другое место в этом же массиве.
  • Array.prototype.fill – заполняет все элементы указанного массива переданным значением.
  • Array.prototype.find – возвращает первое значение, удовлетворяющее условие.
  • Array.prototype.findIndex – возвращает индекс первого значения, удовлетворяющего условие.
  • Array.prototype.keys – возвращает итератор, который выдает последовательность, содержащую ключи массива.
  • Array.prototype.values – возвращает итератор, который выдает последовательность, содержащую значения массива.
  • Array.prototype.entries – возвращает итератор, который выдает последовательность, содержащую пары «ключ–значение» массива.
  • Array.prototype[Symbol.iterator] – абсолютно то же, что и метод <a href="https://ponyfoo.com/articles/es6-array-extensions-in-depth#arrayprototypevalues">Array.prototype.values</a> .
  • Прочитайте ES6 Array Extensions in Depth.


Object



Строки и Unicode



Модули

  • Строгий режим по умолчанию включен в модульной системе ES6.
  • Модули ES6 – это файлы, экспортирующие API.
  • export default value экспортирует код по умолчанию.
  • export var foo = 'bar' экспортирует именованный код.
  • Именованные экспорты – это связи, которые могут быть изменены в любое время из модуля, который их экспортирует.
  • export { foo, bar } экспортирует список именованных экспортов.
  • export { foo as ponyfoo } дает возможность доступа к экспорту по псевдониму ponyfoo .
  • export { foo as default } помечает именованный экспорт как экспорт по умолчанию.
  • Есть хороший способ избежать путаницы: писать в конце всех своих модулей export default api , где api – это объект.
  • Загрузка модулей зависит от имплементации, позволяет взаимодействовать с CommonJS.
  • import 'foo' загружает модуль foo в текущий модуль.
  • import foo from 'ponyfoo' присваивает локальной переменной foo экспорт по умолчанию ponyfoo .
  • import {foo, bar} from 'baz' импортирует именованные экспорты foo и bar из модуля baz .
  • import {foo as bar} from 'baz' импортирует именованный экспорт foo , но назначает псевдоним bar .
  • import {default} from 'foo' также импортирует экспорт по умолчанию.
  • import {default as bar} from 'foo' импортирует экспорт по умолчанию с псевдонимом bar .
  • import foo, {bar, baz} from 'foo' смешивает экспорт по умолчанию foo с именованными экспортами bar и baz в одной инструкции.
  • import * as foo from 'foo' импортирует объект пространства имён:
  • содержит все именованные экспорты в foo[name] ;
  • содержит экспорт по умолчанию в foo.default , если таковой был объявлен в модуле.
  • Прочитайте ES6 Modules Additions in Depth.


Пора отдохнуть от маркированных списков! Я вас предупреждал, что лучше прочитать серию статей целиком. Кстати, вы уже попробовали konami code?

Комментарии (7)


  1. igordata
    13.11.2015 02:22

    > Maps
    Аллилуйя!


  1. TheRabbitFlash
    13.11.2015 09:34
    -8

    Еще немного и JS станет прям AS3


  1. baka_cirno
    13.11.2015 13:15

    Минус в том, что половина этих фич сейчас нуждается в подключении дополнительных скриптов в рантайме. Простая трансляция в ES5 не прокатывает.


    1. trikadin
      13.11.2015 19:24
      +1

      Ну, полифиллы в нашем деле всегда были, есть и будут. На мой взгляд, полифилл — лучшее решение, чем слой абстракции для обеспечения одинаковой работы в разных браузерах с разными API.


  1. some_x
    13.11.2015 14:27

    > Вы избегаете утечек памяти без ручного учета ссылок – думайте о WeakMap как об IDisposable в .NET.
    весьма натянутое сравнение на мой взгляд. Всё же в .NET есть намного более близкий аналог: WeakReference


  1. ElianL
    13.11.2015 16:32
    +1

    Promise.all(...promises) создает промис, который разрешается, когда все промисы ...promises выполнены или один из них отклонен.


    Я конечно уверен всего лишь на 99%, но если хотя бы один из ..promises отклонен то Promise.all тоже будет отклонен


    1. benjie
      13.11.2015 16:54
      +2

      так и есть,

      Промисы создаются в состоянии ожидания и могут быть разрешены, будучи выполненными или отклоненными.