Please note, this is a STATIC archive of website developer.mozilla.org from November 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Включения генераторов (Generator comprehensions)

Эта статья нуждается в редакционном обзоре. Как вы можете помочь.

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

Не в стандарте. Не используйте!
Включения генераторов - нестандартная возможность, и вряд ли когда-либо появится в ECMAScript. В будущем попробуйте использовать generator.
Синтаксис включений генераторов - это выражение, которое позволяет быстро создать новую функцию-генератор из существующего итератора. Включения присутствуют во множестве языков программирования.
 
Ниже показаны отличия от старого синтаксиса генераторных выражений в SpiderMonkey, основанных на предложениях к стандарту ECMAScript 4.
 

Синтаксис

(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 включений

JS1.7/JS1.8 включения убраны из Gecko 46 (баг 1220564).

Старый синтаксис включений (не используйте!):

(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

См. также

Метки документа и участники

Метки: 
 Внесли вклад в эту страницу: forestbird
 Обновлялась последний раз: forestbird,