Обработка каждого предмета в коллекции является очень распространенной операцией. JavaScript предоставляет много способов итерации по сбору, от простой для и для каждого цикла до Map (), Filter () и массивного понимания (вывод массива). В JavaScript 1.7 итераторы и генераторы приносят новые итерационные механизмы в основном синтаксисе JavaScript, а также предоставляют механизмы для настройки поведения для… в и для каждого петли.
Итератор
Итератор - это объект, который каждый раз обращается к элементу в последовательности сбора и отслеживает текущую позицию итераций в этой последовательности. В JavaScript итератор находится объект, который обеспечивает метод следующего (), который возвращает следующий элемент в последовательности. Этот метод бросает исключение для остановки, когда все элементы в последовательности пересекаются.
Как только объект итератора создается, он может быть неявно называться явным повторением Next () или с помощью JavaScript для… в и для каждого цикла.
Простой итератор, который итерация над объектами и массивами может быть создан с помощью iterator ():
Кода -копия выглядит следующим образом:
var lang = {name: 'javascript', день рождения: 1995};
var it = итератор (lang);
После завершения инициализации метод следующего () может быть вызван для доступа к парам ключевой стоимости объекта по очереди:
Кода -копия выглядит следующим образом:
var pare = it.next (); // Пары клавиш значения [«Имя», "JavaScript"]
pair = it.next (); // Пара ключевых значений ["День рождения", 1995]
pair = it.next (); // было брошено исключение `` stopiteration '
Для… в цикле можно использовать для замены явного вызова на следующий () метод. Когда исключение остановки будет брошено, петля автоматически завершится.
Кода -копия выглядит следующим образом:
var it = итератор (lang);
Для (пара в нем)
print (пара); // одна [ключ, значение] пара клавиш в нем каждый раз выводится
Если вы хотите только итерацию по значению ключа объекта, вы можете передать второй параметр в функцию iterator (), со значением True:
Кода -копия выглядит следующим образом:
var it = итератор (lang, true);
для (var -ключ в нем)
print (Key); // только выходное значение ключа
Одним из преимуществ использования iterator () для доступа к объектам является то, что пользовательские свойства, добавленные в Object.prototype, не включены в объект последовательности.
Итератор () также может использоваться на массиве:
Кода -копия выглядит следующим образом:
var langs = ['javascript', 'python', 'haskell'];
var it = итератор (Langs);
Для (пара в нем)
print (пара); // Только вывод итерации [индекс, язык] пара ключевых значений
Так же, как пересечение объекта, результатом передачи верного в обход в качестве второго параметра будет индекс массива:
Кода -копия выглядит следующим образом:
var langs = ['javascript', 'python', 'haskell'];
var it = итератор (langs, true);
для (var i в этом)
print (i); // Вывод 0, затем 1, затем 2
Используйте ключевое слово let для назначения индексов и значений для блокировки переменных отдельно в цикле, и вы также можете уничтожить назначение (назначение деструкции):
Кода -копия выглядит следующим образом:
var langs = ['javascript', 'python', 'haskell'];
var it = итераторы (Langs);
для (пусть [я, lang] в нем)
print (i + ':' + lang); // вывод "0: javaScript" и т. Д.
Объявить пользовательский итератор
Некоторые объекты, представляющие набор элементов, должны быть имерными способами.
1. Итерация над объектом, представляющим диапазон (диапазон), должен возвращать число, содержащее в этом диапазоне один за другим.
2. Листовый узел дерева можно получить с помощью глубины-первой или ширины
3. Итерация над объектом, представляющим результаты запроса базы данных, должны возвращать строку по строке, даже если весь набор результатов не был загружен в один массив.
4. Итераторы, действующие на бесконечную математическую последовательность (например, последовательность Fibonacci) должны возвращать результаты один за другим, не создавая структуру данных бесконечной длины.
JavaScript позволяет написать код, который настраивает итерационную логику и применить его к объекту
Мы создаем простой объект диапазона с двумя значениями:
Кода -копия выглядит следующим образом:
Диапазон функций (низкий, высокий) {
this.low = low;
это. High = High;
}
Теперь мы создаем пользовательский итератор, который возвращает последовательность всех целых чисел в диапазоне. Интерфейс итератора требует, чтобы мы предоставили метод следующего () для возврата следующего элемента в последовательности или бросить исключение для остановки.
Кода -копия выглядит следующим образом:
Функция ляточности (диапазон) {
this.range = range;
this.current = this.range.low;
}
Rangeiterator.prototype.next = function () {
if (this.current> this.range.high)
бросить остановку;
еще
вернуть это.current ++;
};
Наш динамика создается экземпляр экземпляром диапазона при сохранении текущего свойства для отслеживания местоположения текущей последовательности.
Наконец, для того, чтобы в диапазоне было объединено в диапазон, нам нужно добавить специальный метод __iterator__. Когда мы пытаемся итерацию в течение диапазона, он будет вызван и должен вернуть экземпляр динамики, который реализует итерационную логику.
Кода -копия выглядит следующим образом:
Range.prototype .__ iterator__ = function () {
вернуть новый динамика (это);
};
После завершения нашего индивидуального итератора мы можем перевернуть экземпляр сфера:
Кода -копия выглядит следующим образом:
var range = новый диапазон (3, 5);
для (var i в диапазоне)
print (i); // Вывод 3, затем 4, затем 5
Генератор: лучший способ построить итераторы
Хотя пользовательские итераторы являются полезным инструментом, вам необходимо тщательно спланировать их при создании, потому что их нужно явно поддерживать.
Генератор обеспечивает мощные функции: он позволяет вам определять функцию, которая содержит ваш собственный итерационный алгоритм, и может автоматически поддерживать свое состояние.
Генератор - это специальная функция, которую можно использовать в качестве фабрики итератора. Если функция содержит одно или несколько выражений доходности, она называется генератором (примечание переводчика: node.js также должен быть представлен * до имени функции).
ПРИМЕЧАНИЕ. Ключевое слово доходности может использоваться только для блоков кода в HTML, которые включены в <script type = "Application/javascript; version = 1,7"> (или позже). Теги скрипта XUL (XML пользовательского интерфейса) не требуют указания этого специального блока кода для доступа к этим функциям.
Когда вызывается функция генератора, корпус функции не будет выполнена немедленно, он вернет объект генератора-иранторов. Каждый раз, когда называется метод следующего () генератора-ирантора, корпус функции будет выполняться до следующего выражения урожая, а затем возвращает его результат. Когда функция заканчивается или сталкивается с оператором возврата, будет брошено исключение для остановки.
Используйте пример, чтобы лучше проиллюстрировать:
Кода -копия выглядит следующим образом:
function simplegenerator () {
урожай "первой";
урожай "второй";
урожай "третий";
для (var i = 0; i <3; i ++)
Уход I;
}
var g = simplegenerator ();
print (g.next ()); // вывод "первым"
print (g.next ()); // вывод "второй"
print (g.next ()); // вывод "третий"
print (g.next ()); // Вывод 0
print (g.next ()); // Вывод 1
print (g.next ()); // Вывод 2
print (g.next ()); // бросить исключение для остановки
Функции генератора могут быть использованы непосредственно классом как метод __iterator__ и могут эффективно уменьшить количество кода, где необходимы пользовательские итераторы. Давайте переписаем диапазон, используя генератор:
Кода -копия выглядит следующим образом:
Диапазон функций (низкий, высокий) {
this.low = low;
это. High = High;
}
Range.prototype .__ iterator__ = function () {
для (var i = this.low; i <= this.high; i ++)
Уход I;
};
var range = новый диапазон (3, 5);
для (var i в диапазоне)
print (i); // Вывод 3, затем 4, затем 5
Не все генераторы прекратятся, вы можете создать генератор, который представляет бесконечную последовательность. Следующий генератор реализует последовательность Fibonacci, в которой каждый элемент является суммой первых двух:
Кода -копия выглядит следующим образом:
функция fibonacci () {
var fn1 = 1;
var fn2 = 1;
while (1) {
var current = fn2;
fn2 = fn1;
fn1 = fn1 + ток;
текущий ток;
}
}
var sequence = fibonacci ();
print (sequence.next ()); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
print (sequence.next ()); // 5
print (sequence.next ()); // 8
print (sequence.next ()); // 13
Функции генератора могут принимать параметры и будут использовать эти параметры, когда функция вызывает в первый раз. Генератор может быть прекращен (заставляя его бросить исключение для остановки) с помощью оператора возврата. Следующий вариант Fibonacci () принимает необязательный предельный параметр, который завершает функцию при запуска условии.
Кода -копия выглядит следующим образом:
Функция fibonacci (Limit) {
var fn1 = 1;
var fn2 = 1;
while (1) {
var current = fn2;
fn2 = fn1;
fn1 = fn1 + ток;
if (Limit && Current> Limit)
возвращаться;
текущий ток;
}
}
Усовершенствованные функции генератора
Генератор может рассчитать возвращаемое значение дохода на основе требований, что делает его представляющим ранее дорогие требования расчета последовательности, даже бесконечную последовательность, показанную выше.
В дополнение к следующему () методу, объект генератора-ирантора также имеет метод Send (), который может изменить внутреннее состояние генератора. Стоимость, передаваемое Send (), будет рассматриваться как результат последнего выражения урожайности, и генератор будет приостановлен. Перед тем, как передать указанное значение, используя метод Send (), вы должны позвонить следующему () хотя бы один раз, чтобы запустить генератор.
Следующий генератор Fibonacci использует метод Send () для перезапуска последовательности:
Кода -копия выглядит следующим образом:
функция fibonacci () {
var fn1 = 1;
var fn2 = 1;
while (1) {
var current = fn2;
fn2 = fn1;
fn1 = fn1 + ток;
var reset = текущий ток;
if (сбросить) {
fn1 = 1;
fn2 = 1;
}
}
}
var sequence = fibonacci ();
print (sequence.next ()); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
print (sequence.next ()); // 5
print (sequence.next ()); // 8
print (sequence.next ()); // 13
print (sequence.send (true)); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
Примечание: Интересно, что вызов Send (неопределенная) точно такой же, как и вызов Next (). Однако, когда метод Send () вызовет для запуска нового генератора, будет брошено исключение TypeError, за исключением неопределенного.
Вы можете вызвать метод броска и передать выброс, который он должен бросить, чтобы заставить генератор бросить исключение. Это исключение будет выброшено из текущего контекста и приостановил генератор, аналогичный текущему выполнению доходности, но заменено оператором значения броска.
Если доход не встречается во время процесса бросания исключения, исключение будет передано до тех пор, пока не будет вызван метод Throd (), а впоследствии вызовет Next (), вызовет исключение для остановки.
Генератор имеет метод Close (), чтобы заставить генератор до конца. Завершение генератора будет иметь следующие эффекты:
1. Все действительные, наконец, предложения в генераторе будут выполнены
2. Если наконец -то слово бросает любое исключение, кроме остановки, исключение будет передано вынуждению метода Close ().
3. Генератор прекратится
Выражения генератора
Одним из очевидных недостатков вывода массива является то, что они приводят к созданию всего массива в памяти. Накладные расходы ввод в вывод незначительны, когда его накладные расходы являются самой небольшой массивом - однако проблемы могут возникнуть, когда входной массив большой или при создании нового дорогостоящего (или бесконечного) генератора массива.
Генератор позволяет ленивым вычислениям вычислять элементы по мере необходимости при необходимости. Выражения генератора синтаксически почти одинаковы, что и вывод массива - он использует скобки вместо квадратных кронштейнов (и использует для ... вместо каждого ... в) - но он создает генератор вместо массива, чтобы расчет можно было отложить. Вы можете думать об этом как о кратком синтаксисе для создания генератора.
Предположим, что у нас есть итератор для итерации по огромной последовательности целых чисел. Нам нужно создать новый итератор для итерации по четным числам. Вывод массива создаст целый массив, содержащий все четные числа в памяти:
Кода -копия выглядит следующим образом:
var doubles = [i * 2 для (i in in It)];
Выражение генератора создаст новый итератор и рассчитывает равномерное значение по мере необходимости:
Кода -копия выглядит следующим образом:
var it2 = (i * 2 для (i в нем));
print (it2.next ()); // первое равномерное в нем
print (it2.next ()); // Второе четное число в нем
Когда генератор используется в качестве параметра функции, скобки используются в качестве функционального вызова, что означает, что самые внешние скобки могут быть опущены:
Кода -копия выглядит следующим образом:
var result = dosomething (i * 2 для (i в нем));
Конец.