Моя серия заметок ES6 in Depth, состоящая из 24 записей, описывает большинство синтаксических изменений и нововведений в ES6. В этой публикации я подведу итог всего изложенного в предыдущих статьях, чтобы дать возможность посмотреть еще раз на всё вместе.
Содержание
Часть первая: здесь.
Символы
Итераторы
Генераторы
Промисы
Maps
WeakMaps
Sets
WeakSets
Прокси
Reflection
Строки и Unicode
Модули
Пора отдохнуть от маркированных списков! Я вас предупреждал, что лучше прочитать серию статей целиком. Кстати, вы уже попробовали konami code?
Содержание
- Введение
- Инструментарий
- 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]]
в newMap(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
Object.assign
– рекурсивная перезапись мелких свойств изtarget, ...objects.
Object.is
– похоже на использование оператора===
, но также возвращаетtrue
для парNaN === NaN
и+0 === -0
.Object.getOwnPropertySymbols
– возвращает все собственные символы объекта.Object.setPrototypeOf
– изменяет прототип. Эквивалентен сеттеруtarget.__proto__
.- Посмотрите также раздел «Литералы объектов».
- Прочитайте ES6
Object
Changes in Depth.
Строки и Unicode
- Операции со строками:
String.prototype.startsWith
– проверяет, что строка начинается со значения;String.prototype.endsWith
– проверяет, что строка заканчивается значением;String.prototype.includes
– проверяет, что строка содержит значение;String.prototype.repeat
– возвращает строку, повторенную указанное количество раз;String.prototype[Symbol.iterator]
– дает возможность итерировать по последовательности символов юникода (не самим символам).- Unicode
String.prototype.codePointAt
– номер символа юникода в десятичной системе для элемента строки, указанного при помощи индекса;String.fromCodePoint
– дано...codepoints
, возвращает строку из соответствующих символов в unicode;String.prototype.normalize
– возвращает нормализованную версию представления строки в unicode.- ?Прочитайте ES6 Strings and Unicode Additions in Depth.
Модули
- Строгий режим по умолчанию включен в модульной системе 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)
baka_cirno
13.11.2015 13:15Минус в том, что половина этих фич сейчас нуждается в подключении дополнительных скриптов в рантайме. Простая трансляция в ES5 не прокатывает.
trikadin
13.11.2015 19:24+1Ну, полифиллы в нашем деле всегда были, есть и будут. На мой взгляд, полифилл — лучшее решение, чем слой абстракции для обеспечения одинаковой работы в разных браузерах с разными API.
some_x
13.11.2015 14:27> Вы избегаете утечек памяти без ручного учета ссылок – думайте о WeakMap как об IDisposable в .NET.
весьма натянутое сравнение на мой взгляд. Всё же в .NET есть намного более близкий аналог: WeakReference
ElianL
13.11.2015 16:32+1Promise.all(...promises) создает промис, который разрешается, когда все промисы ...promises выполнены или один из них отклонен.
Я конечно уверен всего лишь на 99%, но если хотя бы один из ..promises отклонен то Promise.all тоже будет отклоненbenjie
13.11.2015 16:54+2так и есть,
Промисы создаются в состоянии ожидания и могут быть разрешены, будучи выполненными или отклоненными.
igordata
> Maps
Аллилуйя!