Iterator迭代器知识点总结-JavaScript

123 阅读3分钟

1.Iterator的概念

Iterator是一个统一的接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作,所以也可以叫做遍历器。

作用:

1.为各种数据结构,提供一个统一的、简便的访问接口

2.使得数据结构的成员能够按某种次序排列

3.ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费

2. Iterator 接口

2.1 Symbol.iterator属性

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性

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

注:obj具备Symbol.iterator属性,是可遍历的

Symbol.iterator是遍历器生成函数,返回一个遍历器对象

2.2 迭代过程

  1. 通过 Symbol.iterator 创建一个迭代器,指向当前数据结构的起始位置
  2. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  4. 每调用 next 方法返回一个包含 value 和 done 属性的对象

value 是当前属性的值, done 用于判断是否遍历结束,当 done 为 true 时则遍历结束

2.3 为对象添加 Iterator 接口

let obj = {
  data: [ 'hello', 'world' ],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

3. 调用 Iterator 接口的场合

1)解构赋值

对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。

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']

(3)yield*

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)其他场合

由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。

  • for...of
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]])
  • Promise.all()
  • Promise.race()

4. 原生具备 Iterator 接口的数据结构

1. Array

2. Map

3. Set

4. TypedArray(类数组对象)

4.1 String

4.2 函数的 arguments 对象

4.3 DOM NodeList 对象

并不是所有类似数组的对象都具有 Iterator 接口,

所以建议使用Array.from方法Array.prototype.slice.call(obj)将其转为数组

Array的Iterator接口

JavaScript 原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6 提供for...of循环,允许遍历获得键值。

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

var iterator = arr[Symbol.iterator]();

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

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

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

Set 和 Map的 Iterator 接口

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

String的 Iterator 接口

var someString = "hi";
typeof someString[Symbol.iterator]
// "function"

var iterator = someString[Symbol.iterator]();

iterator.next()  // { value: "h", done: false }
iterator.next()  // { value: "i", done: false }
iterator.next()  // { value: undefined, done: true }

DOM NodeList对象和arguments对象的Iterator接口

// 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'