Iterator与for...of小结

238 阅读2分钟

1. 背景

ES6 借鉴 C++、Java、C# 和 Python 语言,引入了for...of循环,作为遍历所有数据结构的统一的方法。

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。

即,Iterator就是一个遍历接口。

2. 目前已实现iterator的数据类型

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

以Array为例:

const arr = ['a', 'b', 'c', 'd']
for (const value of arr) {
    console.log(value) // a b c d 
}

image.png

3. Iterator实现原理

引用官方的表达:

image.png

判断结束就是返回的数据对象中的done为true。 // {done: true, value: undefined}

4. Object类型如何创建Iterator遍历接口

4.1 方案一:Generator封装

从文章第三章可以发现,其实底层就是Generator函数, 我们用Generator包一下

jsconst 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

4.2 方案二:借用数组的Iterator

Array对象是具备可迭代的,但前提是key值为数组的下标,这类对象是比较极端的,不是通用做法,仅用来开拓思路。

let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}

4.3 方案三:自定义Iterator方案

既然我们制定for...of是通过一次次调用next方法来获取每一次轮训的值

let obj = {
    a:1,
    b:2,
    [Symbol.iterator](){
        let index = 0;
        return {
            next: () => {
                let keys = Object.keys(this);
                return index >= keys.length ? {done: true} : {value: this[keys[index++]]}
            }
        }
    }
};
for (let val of obj) {
    console.log(val);// 1, 2
}

5. 遍历api小结

数组对象为例

5.1 for循环

1.使用功能最全,是最原始的遍历写法。 2.写起来比较不舒服,比较麻烦。所以数组对象提供了forEach和map方法

5.2 forEach/map

1.数组全变量遍历,api轻巧。 2.循环无法中途退出,return和break命令皆不奏效

5.3 for...in

1.api轻巧、方便。 2.for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。for...in本意是为遍历对象设计。

5.4 for...of

1.api轻巧、方便。 2.可以配合continue、break、return使用,灵活度较高 3. 前提是该遍历类型已经实现Iterator接口