Чтение онлайн

ЖАНРЫ

JavaScript. Подробное руководство, 6-е издание
Шрифт:

// Вернуть объект, представляющий диапазон.

// Границы диапазона не изменяются

// и хранятся в замыкании.

// Диапазоны могут проверять вхождение, max;

// Возвращает итерируемый объект, представляющий диапазон чисел

function range(min,max) { return {

get min { return min; },

get max { return max; },

includes: function(x) {

return min <= x && x <= max;

},

toString: function { //
Диапазоны имеют строковое представление,

return "[" + min + "," + max + "]";

},

__iterator__: function { // Возможно выполнять итерации по диапазону

let val = Math.ceil(min); // Сохранить текущ. позицию в замыкании.

return { // Вернуть объект-итератор.

next: function { // Вернуть следующее число в диапазоне.

if (val > max) // Если достигнут конец - прервать итерации

throw StopIteration;

return val++; // Иначе вернуть следующее число

} // и увеличить позицию

};

}

};

}

// Далее демонстрируется, как можно выполнять итерации по диапазону:

for(let і in range(1,10)) console.log(i); // Выведет числа от 1 до 10

Обратите внимание, что, несмотря на необходимость писать метод

__iterator__
и возбуждать исключение
Stoplteration
для создания итерируемых объектов и их итераторов, вам не придется (в обычной ситуации) вызывать метод
__iterator__
и/или обрабатывать исключение
Stoplteration
– все это сделает цикл
for/in
. Если по каким-то причинам потребуется явно получить объект-итератор итерируемого объекта, можно воспользоваться функцией
Iterator
. (
Iterator
– это глобальная функция, которая появилась в версии JavaScript 1.7.) Если передать этой функции итерируемый объект, она просто вернет результат вызова метода
__iterator__
, что придаст дополнительную ясность программному коду. (Если передать функции
Iterator
второй аргумент, она передаст его методу
__iterator__
.)

Однако функция

Iterator
имеет еще одно важное назначение. Если ей передать объект (или массив), не имеющий метода
__iterator__
, она вернет собственную реализацию итерируемого итератора для объекта. Каждый вызов метода
next
этого итератора будет возвращать массив с двумя значениями. В первом элементе массива будет возвращаться имя свойства объекта, а во втором - значение этого свойства. Поскольку этот объект является итерируемым итератором, его можно использовать в цикле
for/in
вместо прямого вызова метода
next,
а это означает, что функцию
Iterator
можно использовать совместно с операцией присваивания с разложением при выполнении итераций по свойствам и значениям объекта или массива:

for(let [k,v] in Iterator({a:1,b:2})) // Итерации по ключам и значениям

console.log(k + "=" + v); // Выведет "a=1" и "b=2"

Итератор, возвращаемый функцией

Iterator,
имеет еще две важные особенности. Во-первых, он игнорирует унаследованные свойства и выполняет итерации только по «собственным» свойствам, что чаще всего и требуется. Во-вторых, если передать функции
Iterator
значение
true
во втором аргументе, возвращаемый итератор будет выполнять итерации только по именам свойств, без их значений. Обе эти особенности демонстрируются в следующем
примере:

о={х:1,у:2} // Объект с двумя свойствами

Object.prototype.z = 3; // Теперь все объекты унаследуют z

for(p in о) console.log(p); // Выведет "x", "у" и ”z"

for(р in Iterator(o, true)) console.log(p); // Выведет только “x" и "у"

11.4.3. Генераторы

Генераторы - это особенность JavaScript 1.7 (заимствованная из языка Python), основанная на использовании нового ключевого слова

yield
. Программный код, использующий данную особенность, должен явно указать номер версии 1.7, как описывалось в разделе 11.2. Ключевое слово
yield
используется в функциях и действует аналогично инструкции
return
, возвращая значение из функции. Разница между
yield
и
return
состоит в том, что функция, возвращающая значение с помощью ключевого слова
yield
, сохраняет информацию о своем состоянии, благодаря чему ее выполнение может быть возобновлено. Такая способность к возобновлению выполнения делает
yield
замечательным инструментом для создания итераторов. Генераторы - очень мощная особенность языка, но понять принцип их действия совсем не просто. Для начала познакомимся с некоторыми определениями.

Любая функция, использующая ключевое слово

yield
(даже если инструкция
yield
никогда не будет выполняться), является функцией-генератором. Функции-генераторы возвращают значения с помощью
yield
. Они могут использовать инструкцию
return
без значения, чтобы завершиться до того, как будет достигнут конец тела функции, но они не могут использовать
return
со значением. За исключением использования ключевого слова
yield
и ограничений на использование инструкции
return
, функции-генераторы ничем не отличаются от обычных функций: они объявляются с помощью ключевого слова
function
, оператор
typeof
возвращает для них строку «function» и как обычные функции они наследуют свойства и методы от
Function.prototype
. Однако поведение функции-генератора совершенно отличается от поведения обычной функции: при вызове, вместо того чтобы выполнить свое тело, функция-генератор возвращает объект генератора.

Генератор - это объект, представляющий текущее состояние функции-генератора. Он определяет метод

next,
вызов которого возобновляет выполнение функции-генератора и позволяет продолжить ее выполнение, пока не будет встречена следующая инструкция
yield
. Когда это происходит, значение в инструкции
yield
в функции-генераторе становится возвращаемым значением метода
next
генератора. Если функция-генератор завершает свою работу вызовом инструкции
return
или в результате достижения конца своего тела, метод
next
генератора возбуждает исключение
StopIteration
.

Тот факт, что генераторы имеют метод

next,
который может возбуждать исключение
StopIteration
, явственно говорит о том, что они являются итераторами. [22] В действительности они являются итерируемыми итераторами, т. е. они могут использоваться в циклах
for/in
. Следующий пример демонстрирует, насколько просто создавать функции-генераторы и выполнять итерации по значениям, которые они возвращают с помощью инструкции
yield
:

22

Генераторы иногда называют «итераторами-генераторами», чтобы отличить их от создающих их функций-генераторов. В этой главе вместо термина «итераторы-генераторы» мы будем использовать термин «генераторы». В других источниках вы можете встретить термин «генератор», обозначающий одновременно и функции-генераторы, и итераторы-генераторы.

Поделиться с друзьями: