Итераторы серии ES6 и для

внешний интерфейс GitHub Babel ECMAScript 6
Итераторы серии ES6 и для

источник

Стандарт для кода цикла:

var colors = ["red", "green", "blue"];

for (var i = 0, len = colors.length; i < len; i++) {
    console.log(colors[i]);
}

Выглядит очень просто, но оглядываясь назад на этот код, на самом деле нам нужны только значения элементов в массиве, но нужно заранее получить длину массива, объявить индексные переменные и т.д., особенно когда несколько циклы вложены, нам нужно использовать с несколькими индексными переменными, сложность кода значительно возрастет.Например, мы используем двойные циклы для дедупликации:

function unique(array) {
    var res = [];
    for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {
        for (var j = 0, resLen = res.length; j < resLen; j++) {
            if (array[i] === res[j]) {
                break;
            }
        }
        if (j === resLen) {
            res.push(array[i]);
        }
    }
    return res;
}

Чтобы устранить эту сложность и уменьшить количество ошибок в циклах (таких как неправильное использование переменных в других циклах), ES6 предоставляет итераторы и циклы for of.решить вместеЭта проблема.

итератор

Так называемый итератор, на самом деле, метод объекта next() имеет, next возвращает объект результата при каждом вызове(), объект результата имеет два свойства, значение представляет текущее значение, выполнено указывает ли конец хода.

Мы создаем итератор напрямую, используя синтаксис ES5:

function createIterator(items) {
    var i = 0;
    return {
        next: function() {
            var done = i >= item.length;
            var value = !done ? items[i++] : undefined;

            return {
                done: done,
                value: value
            };
        }
    };
}

// iterator 就是一个迭代器对象
var iterator = createIterator([1, 2, 3]);

console.log(iterator.next()); // { done: false, value: 1 }
console.log(iterator.next()); // { done: false, value: 2 }
console.log(iterator.next()); // { done: false, value: 3 }
console.log(iterator.next()); // { done: true, value: undefined }

for of

В дополнение к итераторам нам также нужен способ обхода объектов итераторов. ES6 предоставляет оператор for of. Давайте воспользуемся for of для обхода объектов итераторов, созданных в предыдущем разделе. Попробуйте:

var iterator = createIterator([1, 2, 3]);

for (let value of iterator) {
    console.log(value);
}

ошибка результатаTypeError: iterator is not iterable, указывая на то, что сгенерированный нами объект итератора не является итерируемым (проходимым).

Итак, что можно пройти?

Фактически, пока структура данных развернута с интерфейсом Iterator, мы называем эту структуру данных «итерируемой».

ES6 предусматривает, что интерфейс Iterator по умолчанию развертывается в свойстве Symbol.iterator структуры данных, или структура данных может считаться "итерируемой", если она имеет свойство Symbol.iterator.

Например:

const obj = {
    value: 1
};

for (value of obj) {
    console.log(value);
}

// TypeError: iterator is not iterable

Если мы напрямую for или обойдем объект, будет сообщено об ошибке, но если мы добавим к объекту свойство Symbol.iterator:

const obj = {
    value: 1
};

obj[Symbol.iterator] = function() {
    return createIterator([1, 2, 3]);
};

for (value of obj) {
    console.log(value);
}

// 1
// 2
// 3

Из этого мы также можем узнать, что for traverses на самом деле является свойством объекта Symbol.iterator.

проходимый объект по умолчанию

Однако, если мы перебираем объект массива напрямую:

const colors = ["red", "green", "blue"];

for (let color of colors) {
    console.log(color);
}

// red
// green
// blue

Хотя мы не добавляли свойство Symbol.iterator вручную, мы все равно можем успешно пройти, потому что ES6 развертывает свойство Symbol.iterator по умолчанию.Конечно, мы также можем вручную изменить это свойство:

var colors = ["red", "green", "blue"];

colors[Symbol.iterator] = function() {
    return createIterator([1, 2, 3]);
};

for (let color of colors) {
    console.log(color);
}

// 1
// 2
// 3

В дополнение к массивам некоторые структуры данных имеют свойство Symbol.iterator, развернутое по умолчанию.

Таким образом, диапазон циклов for...of, которые можно использовать, включает:

  1. множество
  2. Set
  3. Map
  4. Массивоподобные объекты, такие как объект arguments, объект DOM NodeList
  5. Генератор объекта
  6. нить

Макетная реализация для

На самом деле смоделировать реализацию for of относительно просто: нужно получить объект итератора через свойство Symbol.iterator, а затем использовать while для его обхода:

function forOf(obj, cb) {
    let iterable, result;

    if (typeof obj[Symbol.iterator] !== "function")
        throw new TypeError(result + " is not iterable");
    if (typeof cb !== "function") throw new TypeError("cb must be callable");

    iterable = obj[Symbol.iterator]();

    result = iterable.next();
    while (!result.done) {
        cb(result.value);
        result = iterable.next();
    }
}

встроенный итератор

Например, чтобы лучше получить доступ к содержимому объекта, иногда нам нужно только значение в массиве, но иногда нам нужно использовать не только значение, но и индекс.ES6 имеет встроенные следующие три итерации для массив, карта и набор устройств сбора:

  1. entry() возвращает объект итератора для обхода массива [имя ключа, значение ключа]. Для массивов имя ключа является значением индекса.
  2. keys() возвращает объект итератора, который перебирает все имена ключей.
  3. values() возвращает объект итератора, который выполняет итерацию по всем значениям ключа.

Возьмите массив в качестве примера:

var colors = ["red", "green", "blue"];

for (let index of colors.keys()) {
    console.log(index);
}

// 0
// 1
// 2

for (let color of colors.values()) {
    console.log(color);
}

// red
// green
// blue

for (let item of colors.entries()) {
    console.log(item);
}

// [ 0, "red" ]
// [ 1, "green" ]
// [ 2, "blue" ]

Тип Map подобен массиву, но для типа Set необходимо отметить следующее:

var colors = new Set(["red", "green", "blue"]);

for (let index of colors.keys()) {
    console.log(index);
}

// red
// green
// blue

for (let color of colors.values()) {
    console.log(color);
}

// red
// green
// blue

for (let item of colors.entries()) {
    console.log(item);
}

// [ "red", "red" ]
// [ "green", "green" ]
// [ "blue", "blue" ]

Keys() и values() типа Set возвращают один и тот же итератор, а это означает, что в такой структуре данных, как Set, имя ключа совпадает со значением ключа.

И у каждого типа коллекции есть итератор по умолчанию, который используется в цикле for-of, если он не указан явно. Итератором по умолчанию для массивов и коллекций Set является метод values(), а итератором по умолчанию для коллекций Map — метод entry().

Вот почему direct for of traverses устанавливает и отображает структуры данных и возвращает разные структуры данных:

const values = new Set([1, 2, 3]);

for (let value of values) {
    console.log(value);
}

// 1
// 2
// 3
const values = new Map([["key1", "value1"], ["key2", "value2"]]);
for (let value of values) {
    console.log(value);
}

// ["key1", "value1"]
// ["key2", "value2"]

При обходе структуры данных карты вы можете комбинировать деструктуризацию и присваивание следующим образом:

const valuess = new Map([["key1", "value1"], ["key2", "value2"]]);

for (let [key, value] of valuess) {
    console.log(key + ":" + value);
}

// key1:value1
// key2:value2

Как Babel компилируется для of

Мы можем найти его в БабелеTry it outПросмотрите скомпилированные результаты в:

const colors = new Set(["red", "green", "blue"]);

for (let color of colors) {
    console.log(color);
}

Для такого фрагмента кода результат компиляции выглядит следующим образом:

"use strict";

var colors = new Set(["red", "green", "blue"]);

var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
    for (
        var _iterator = colors[Symbol.iterator](), _step;
        !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
        _iteratorNormalCompletion = true
    ) {
        var color = _step.value;

        console.log(color);
    }
} catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
} finally {
    try {
        if (!_iteratorNormalCompletion && _iterator.return) {
            _iterator.return();
        }
    } finally {
        if (_didIteratorError) {
            throw _iteratorError;
        }
    }
}

По крайней мере, как видно из результатов компиляции, используяfor ofЗа циклом по-прежнему используется интерфейс Symbol.iterator.

И этот скомпилированный код немного сложен, здесь есть два раздела, один из которых — цикл for:

for (
    var _iterator = colors[Symbol.iterator](), _step;
    !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
    _iteratorNormalCompletion = true
) {
    var color = _step.value;
    console.log(color);
}

Он немного отличается от стандартного написания цикла, давайте посмотрим на синтаксис оператора for:

for (initialize; test; increment) statement;

Выражения initialize, test и increment разделены точкой с запятой, которая отвечает за初始化操作,循环条件判断а также计数器变量的更新.

Оператор for на самом деле эквивалентен:

initialize;
while (test) {
    statement;
    increment;
}

Логика кода такова: сначала инициализируйте, затем выполняйте тестовое выражение перед каждым выполнением цикла и оценивайте результат выражения, чтобы решить, выполнять ли тело цикла.Если результат теста верен, выполните оператор в теле цикла. . Наконец, выполняется выражение приращения.

И стоит отметить, что любое из трех выражений в цикле for можно проигнорировать, но точку с запятой все равно нужно прописать.

Напримерfor(;;), но это бесконечный цикл...

Например:

var i = 0,
    len = colors.length;
for (; i < len; i++) {
    console.log(colors[i]);
}

Другой пример:

var i = 0,
    len = colors.length;
for (; i < len; ) {
    i++;
}

Затем давайте посмотрим на это выражение цикла for, скомпилированное Babel:

for (
    var _iterator = colors[Symbol.iterator](), _step;
    !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
    _iteratorNormalCompletion = true
) {
    var color = _step.value;
    console.log(color);
}

Запись while эквивалентна:

var _iterator = colors[Symbol.iterator](),
    _step;
while (!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) {
    var color = _step.value;
    console.log(color);
    _iteratorNormalCompletion = true;
}

Легче ли многое понять, и тогда вы обнаружите, что, на самом деле,_iteratorNormalCompletion = trueЭта фраза совершенно не нужна...

Другой немного более сложный фрагмент кода:

try {
  ...
} catch (err) {
  ...
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator.return) {
      _iterator.return();
    }
  } finally {
    ...
  }
}

потому что_iteratorNormalCompletion = (_step = _iterator.next()).done, поэтому _iteratorNormalCompletion указывает, завершен ли процесс полной итерации.Если обычная итерация не завершена, а итератор имеет метод возврата, метод будет выполнен.

Причиной этого является упоминание метода возврата итератора.

Цитата учителя Жуань ИфэнНачало работы с ECMAScript 6:

В дополнение к методу next объект обходчика также может иметь метод return и метод throw. Если вы пишете собственную функцию создания объекта обходчика, необходимо развернуть метод next, а метод return и метод throw являются необязательными.

Метод возврата используется, когда цикл for...of завершается досрочно (обычно из-за ошибки или с оператором break или continue). Метод возврата может быть развернут, если объекту необходимо очистить или освободить ресурсы перед завершением обхода.

Мы можем привести пример:

function createIterator(items) {
    var i = 0;
    return {
        next: function() {
            var done = i >= items.length;
            var value = !done ? items[i++] : undefined;

            return {
                done: done,
                value: value
            };
        },
        return: function() {
            console.log("执行了 return 方法");
            return {
                value: 23333,
                done: true
            };
        }
    };
}

var colors = ["red", "green", "blue"];

var iterator = createIterator([1, 2, 3]);

colors[Symbol.iterator] = function() {
    return iterator;
};

for (let color of colors) {
    if (color == 1) break;
    console.log(color);
}
// 执行了 return 方法

Но, как вы можете видеть в скомпилированном коде, функция возврата выполняется только тогда, когда есть функция возврата, значение, возвращаемое в функции возврата, на самом деле не действует...

Но если вы не вернете значение или вернете значение примитивного типа, результат сообщит об ошибке...

TypeError: Iterator result undefined is not an object

Это связано с тем, что метод возврата должен возвращать объект, что, в свою очередь, продиктовано спецификацией генератора...

Короче говоря, если он используется в браузере, возвращаемое значение функции возврата фактически не действует T^T

серия ES6

Адрес каталога серии ES6:GitHub.com/ в настоящее время имеет бриз…

Ожидается, что в серии ES6 будет написано около 20 статей, направленных на углубление понимания некоторых точек знаний ES6, с акцентом на область действия на уровне блоков, шаблоны меток, функции стрелок, реализацию моделирования символов, наборов, карт и обещаний, схему загрузки модулей, асинхронность. обработка и т.п. содержание.

Если есть какие-либо ошибки или неточности, пожалуйста, поправьте меня, большое спасибо. Если вам нравится или у вас есть вдохновение, добро пожаловать в звезду, что также является поощрением для автора.