Эта статья нуждается в редакционном обзоре. Как вы можете помочь.
Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.
Включения генераторов - нестандартная возможность, и вряд ли когда-либо появится в ECMAScript. В будущем попробуйте использовать generator.
Синтаксис
(for (x of iterable) x) (for (x of iterable) if (condition) x) (for (x of iterable) for (y of iterable) x + y)
Описание
Внутри включений генераторов допустимы два типа компонентов:
Конструкция for-of
всегда идёт первой. Таких конструкций может быть несколько.
Существенный недостаток включений массивов - это то, что они создают полносью новый массив в памяти. Даже когда исходный массив небольшой, накладные расходы получаются весьма заметными, а уж когда входные данные - это большой массив или дорогой (или бесконечный) генератор, создание нового массива может быть трудной задачей.
Генераторы позволяют выполнять ленивые вычисления последовательностей, то есть рассчитывать данные только тогда, когда они нужны. Синтаксисы включений генераторов и включений массивов практически аналогичны — первые используют () вместо [] — но вместо массива они создают генератор, который может выполняться лениво. Включения генераторов можно считать просто более кратким способом создания генераторов.
Предположим, у нас есть итератор it
, который итерирует по длинной последовательности чисел. Мы хотим создать новый итератор, который будет итерировать по тем же числам, умноженным на два. Включение массивов создало бы в памяти новый массив с умноженными числами:
var doubles = [for (i in it) i * 2];
А включение генераторов создало бы новый итератор, который высчитывал бы следующее умноженное значение, когда оно нужно:
var it2 = (for (i in it) i * 2); console.log(it2.next()); // Первое значение из it, умноженное на два console.log(it2.next()); // Второе значение из it, умноженное на два
Когда включение генераторов передаётся как аргумент функции, можно обойтись без скобок вокруг включения:
var result = doSomething(for (i in it) i * 2);
Эти два примера отличаются тем, что используя включения генераторов, нам нужно пройтись по структуре объекта только однажды, а используя включения массивов - дважды: сначала при создании включения, затем, при самой итерации.
Примеры
Простое включение генераторов
(for (i of [ 1, 2, 3 ]) i*i ); // Функция-генератор, которая отдаёт 1, 4, 9 и так далее [...(for (i of [ 1, 2, 3 ]) i*i )]; // [1, 4, 9] var abc = [ "A", "B", "C" ]; (for (letters of abc) letters.toLowerCase()); // Функция-генератор, которая отдаёт 'a', 'b' и 'c'
Включение генераторов с условием
var years = [ 1954, 1974, 1990, 2006, 2010, 2014 ]; (for (year of years) if (year > 2000) year); // функция-генератор, которая отдаёт 2006, 2010 и 2014 (for (year of years) if (year > 2000) if(year < 2010) year); // функция-генератор, которая отдаёт 2006, как и функция ниже: (for (year of years) if (year > 2000 && year < 2010) year); // функция-генератор, которая отдаёт 2006
Включение генератора в сравнении с функцией-генератором
Будет проще понять синтаксис включений генераторов, если сравнить его с функцией-генератором.
Пример 1: Простой генератор.
var numbers = [ 1, 2, 3 ]; // Функция-генератор (function*() { for (let i of numbers) { yield i * i; } })() // Включение генератора (for (i of numbers) i*i ); // Результат: в обоих случаях получается генератор, который отдаёт [ 1, 4, 9 ]
Пример 2: Использование if
в генераторе.
var numbers = [ 1, 2, 3 ]; // Функция-генератор (function*() { for (let i of numbers) { if (i < 3) { yield i * 1; } } })() // Включение генератора (for (i of numbers) if (i < 3) i); // Результат: в обоих случаях получается генератор, который отдаёт [ 1, 2 ]
Спецификации
Включения генераторов изначально входили в черновик ECMAScript 6, но их убрали в ревизии 27 (August 2014). Please see older revisions of ES6 for specification semantics.
Поддержка браузерами
Feature | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Начальная поддержка | Нет | 30 (30) | Нет | Нет | Нет |
Feature | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
Начальная поддержка | Нет | Нет | 30.0 (30) | Нет | Нет | Нет |
Особенности реализации в SpiderMonkey
- Нельзя использовать
let
, потому что она доступна только в JS версии 1.7 и тэгах XUL. - Деструктуризация во включениях пока не поддерживается (баг 980828).
Отличия от старых JS1.7/JS1.8 включений
Старый синтаксис включений (не используйте!):
(X for (Y in Z)) (X for each (Y in Z)) (X for (Y of Z))
Отличия:
- Включения в ES7 создают отдельную область видимость для каждой итерации
for
, вместо одной области для всего включения:- Старые:
[...(()=>x for (x of [0, 1, 2]))][1]() // 2
- Новые:
[...(for (x of [0, 1, 2]) ()=>x)][1]() // 1, each iteration creates a fresh binding for x.
- Старые:
- Включения в ES7 начинаются с
for
вместо выражения присваивания:- Старые:
(i * 2 for (i of numbers))
- Новые:
(for (i of numbers) i * 2)
- Старые:
- Включения в ES7 могут содержать несколько
if
иfor
. - Включения в ES7 работают только с итерациями
, но не сfor...of
for...in