[ES6] Обучающий итератор Ruan Yifeng ES6 и цикл for...of

1. Итераторы

1. Концепция

Итератор является таким механизмом. Это интерфейс, обеспечивающий унифицированный механизм доступа к различным структурам данных. Пока любая структура данных развертывает интерфейс Iterator, она может выполнять операцию обхода (то есть обрабатывать по очереди все элементы структуры данных).

Итератор имеет три функции:

  • Один из них — предоставить унифицированный и удобный интерфейс доступа к различным структурам данных;
  • Второй — позволить элементам структуры данных располагаться в определенном порядке;
  • В-третьих, в ES6 создана новая команда обхода for…of loop, а интерфейс Iterator в основном используется для for…of потребления.

2. Принцип работы

  1. Создайте объект-указатель, указывающий на начало текущей структуры данных. Другими словами, объект обхода по существу является объектом указателя.

  2. При первом nextвызове метода объекта указателя указатель может указывать на первый член структуры данных.

  3. При втором вызове метода объекта указателя nextуказатель указывает на второй член структуры данных.

  4. Продолжайте вызывать методы объекта указателя, nextпока он не укажет на конец структуры данных.

При каждом вызове метода nextвозвращается объект, содержащий два свойства valueи . doneСреди них valueатрибут представляет собой значение текущего члена, а doneатрибут представляет собой логическое значение, указывающее, завершен ли обход.

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
    
    
  var nextIndex = 0;
  return {
    
    
    next: function() {
    
    
      return nextIndex < array.length ?
        {
    
    value: array[nextIndex++], done: false} :
        {
    
    value: undefined, done: true};
    }
  };
}

3. Интерфейс Iterator по умолчанию

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

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

Symbol.iteratorСам атрибут является функцией, которая по умолчанию является функцией генерации обходчика текущей структуры данных. Выполнение этой функции возвращает итератор.

const obj = {
    
    
  [Symbol.iterator] : function () {
    
    
    return {
    
    
      next: function () {
    
    
        return {
    
    
          value: 1,
          done: true
        };
      }
    };
  }
};

ES6 создает новый командный for…ofцикл обхода, интерфейс Iterator в основном предназначен для for…ofиспользования.

Собственные данные с интерфейсом итератора (можно for...ofпройти)

  • Множество
  • карта
  • Набор
  • Нить
  • типизированный массив
  • объект аргументов функции
  • Объект NodeList
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

4. При вызове интерфейса Iterator

В некоторых случаях интерфейс Iterator (то есть метод) будет вызываться по умолчанию.Помимо цикла, Symbol.iteratorописанного ниже , есть несколько других случаев.for...of

(1) Деструктивное присвоение

Метод Symbol.iterator будет вызываться по умолчанию при деструктурировании и назначении массивов и структур Set.

let set = new Set().add('a').add('b').add('c');

let [x,y] = set;
// x='a'; y='b'

let [first, ...rest] = set;
// first='a'; rest=['b','c'];

(2) Оператор спреда

Оператор распространения (…) также вызывает интерфейс Iterator по умолчанию.

// 例一
var str = 'hello';
[...str] //  ['h','e','l','l','o']

// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']

(3) выход*

yield*Вслед за проходимой структурой он вызывает интерфейс обхода структуры.

let generator = function* () {
    
    
  yield 1;
  yield* [2,3,4];
  yield 5;
};

var iterator = generator();

iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true } 

(4) Другие случаи

Поскольку обход массива вызовет интерфейс обходчика, любой случай, который принимает массив в качестве параметра, фактически вызывает интерфейс обходчика. Ниже приведены некоторые примеры.

  • для… из
  • Массив.от()
  • Map(), Set(), WeakMap(), WeakSet() (например new Map([['a',1],['b',2]]))
  • Обещание.все()
  • Обещание.гонка()

2. for… цикла

1. Массив

const arr = ['red', 'green', 'blue'];

for(let v of arr) {
    
    
  console.log(v); // red green blue
}

for...ofВместо методов экземпляров массива можно использовать циклы forEach.

const arr = ['red', 'green', 'blue'];

arr.forEach(function (element, index) {
    
    
  console.log(element); // red green blue
  console.log(index);   // 0 1 2
});

Исходный цикл JavaScript for...inможет получить только имя ключа объекта и не может напрямую получить значение ключа. ES6 предоставляет for...ofциклы, позволяющие обход для получения ключевых значений.

var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
    
    
  console.log(a); // 0 1 2 3
}

for (let a of arr) {
    
    
  console.log(a); // a b c d
}

for...ofЦикл вызывает интерфейс итератора, а интерфейс итератора для массивов возвращает только свойства с числовыми индексами. Это for...inне то же самое, что цикл.

let arr = [3, 5, 7];
arr.foo = 'hello';

for (let i in arr) {
    
    
  console.log(i); // "0", "1", "2", "foo"
}

for (let i of arr) {
    
    
  console.log(i); //  "3", "5", "7"
}

В приведенном выше коде for...ofцикл не возвращает свойство foo массива arr.

2. Структура набора и карты

var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
    
    
  console.log(e);
}
// Gecko
// Trident
// Webkit

var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
    
    
  console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-262

3. Массивоподобные объекты

Массивоподобные объекты включают в себя несколько классов. Ниже приведены примеры циклов for...of для строк, объектов DOM NodeList и объектов arguments.

// 字符串
let str = "hello";

for (let s of str) {
    
    
  console.log(s); // h e l l o
}

// DOM NodeList对象
let paras = document.querySelectorAll("p");

for (let p of paras) {
    
    
  p.classList.add("test");
}

// arguments对象
function printArgs() {
    
    
  for (let x of arguments) {
    
    
    console.log(x);
  }
}
printArgs('a', 'b');
// 'a'
// 'b'

Не все массивоподобные объекты имеют интерфейс Iterator, простым решением является использование метода Array.from для преобразования его в массив.

let arrayLike = {
    
     length: 2, 0: 'a', 1: 'b' };

// 报错
for (let x of arrayLike) {
    
    
  console.log(x);
}

// 正确
for (let x of Array.from(arrayLike)) {
    
    
  console.log(x);
}

4. Объект

Для обычных объектов for...ofструктуру нельзя использовать напрямую, и будет сообщено об ошибке, а интерфейс Iterator должен быть развернут, прежде чем его можно будет использовать. Однако в этом случае for...inцикл все еще можно использовать для обхода имен ключей.

let es6 = {
    
    
  edition: 6,
  committee: "TC39",
  standard: "ECMA-262"
};

for (let e in es6) {
    
    
  console.log(e);
}
// edition
// committee
// standard

for (let e of es6) {
    
    
  console.log(e);
}
// TypeError: es6[Symbol.iterator] is not a function

Приведенный выше код указывает, что для обычных объектов for...inцикл может пройти по имени ключа, и for...ofцикл сообщит об ошибке.

Одним из решений является использование Object.keysметода для создания массива имен ключей объекта, а затем обход массива.

for (var key of Object.keys(someObject)) {
    
    
  console.log(key + ': ' + someObject[key]);
}

Другой способ — использовать функцию Generator для переупаковки объекта.

const obj = {
    
     a: 1, b: 2, c: 3 }

function* entries(obj) {
    
    
  for (let key of Object.keys(obj)) {
    
    
    yield [key, obj[key]];
  }
}

for (let [key, value] of entries(obj)) {
    
    
  console.log(key, '->', value);
}
// a -> 1
// b -> 2
// c -> 3

3. Сравнение с другими синтаксисами обхода

Взяв в качестве примера массивы, JavaScript предоставляет разнообразный синтаксис обхода. Самый примитивный способ записи — это цикл for.

for (var index = 0; index < myArray.length; index++) {
    
    
  console.log(myArray[index]);
}

Этот способ записи громоздок, поэтому массивы предоставляют встроенные forEachметоды.

myArray.forEach(function (value) {
    
    
  console.log(value);
});

Проблема с таким способом написания заключается в том, что нет возможности выйти из forEachцикла, и ни breakкоманда, ни returnкоманда не будут работать.

for...inЦикл может перебирать ключи массива.

for (var index in myArray) {
    
    
  console.log(myArray[index]);
}

for...inЦиклы имеют несколько недостатков.

  1. Ключи массива — числа, но for...inциклнитьВ качестве имен ключей «0», «1», «2» и так далее.
  2. for...inЦикл перебирает не только числовые имена ключей, но и другие ключи, добавленные вручную, даже ключи в цепочке прототипов.
  3. В некоторых случаях цикл for...in перебирает ключи в произвольном порядке.

Короче говоря, for...inцикл в основном дляпересекать объектыПо дизайну он не подходит для обхода массивов.

for...ofПо сравнению с несколькими описанными выше подходами зацикливание имеет ряд существенных преимуществ.

for (let value of myArray) {
    
    
  console.log(value);
}
  1. Имеет for...inтакой же лаконичный синтаксис, но без for...inтех недостатков.
  2. В отличие от forEachметодов, его можно break、continue和returnиспользовать с .
  3. Предоставляет унифицированный рабочий интерфейс для обхода всех структур данных.

Ниже приведен пример использования breakоператора для выхода из for...ofцикла.

for (var n of fibonacci) {
    
    
  if (n > 1000)
    break;
  console.log(n);
}

В приведенном выше примере будут выведены элементы, последовательность Фибоначчи которых меньше или равна 1000. Если текущий элемент больше 1000, breakоператор будет использован для выхода for...ofиз цикла.

Supongo que te gusta

Origin blog.csdn.net/Bon_nenul/article/details/128236461
Recomendado
Clasificación