剖析 Iterator 迭代器

90 阅读2分钟

Iterator迭代器

遍历器(Iterator) 是一种机制(接口):为各种不同的数据结构提供统一的访问机制,任何数据结构只要不是 Iterator 接口,就可以完成遍历操作「for of 循环」,依次处理该数据结构的所有成员「拥有 next 方法用于依次遍历数据结构的成员」,每一次遍历返回的结果是一个对象:

{ 
    done: false, // 记录是否遍历完成
    value: xxx  // 当前遍历的结果
}

实现一个 Iterator 类

由于内置没有 Iterator 类,我们根据它的规范,手动实现一个类,接收一个集合「数组、类数组对象」,返回一个迭代器对象。

class Iterator {
    constructor(data) {
      this.data = data;
      this.index = 0;
    }

    next() {
        // 迭代完了
        if (this.index > this.data.length - 1) {
            return {
                 done: true,
                 value: undefined
            }
        }

        return {
            done: false,
            value: this.data[this.index++]
        }
    }
}

let itor = new Iterator([10, 20, 30, 40]);

console.log(itor.next());
console.log(itor.next());
console.log(itor.next());
console.log(itor.next());
console.log(itor.next());

Symbol.iterator

虽然浏览器没有内置的 Iterator,但是它给很多数据结构,都提供了迭代的接口方法 Symbol.iterator,具备这个接口的数据结构,就可以基于 for of 进行循环迭代了

  1. 数组、类数组「arguments/NodeList/HTMLCollection」、字符串、Set/Map、generator object..
  2. Object.prototype 上是不具备 Symbol.iterator
// ƒ values() { [native code] }
console.log(Array.prototype[Symbol.iterator]);
console.log(String.prototype[Symbol.iterator]);
console.log(NodeList.prototype[Symbol.iterator]);
console.log(HTMLCollection.prototype[Symbol.iterator]);
console.log(Set.prototype[Symbol.iterator]); 
console.log(Map.prototype[Symbol.iterator]); 

修改 Symbol.iterator

// for of 遍历的时候,先调用对象的 [Symbol.iterator],获取一个迭代器实例 itor
// 每一轮循环,都是 itor.next() 执行,并且把返回对象的 value 值拿到
// 当返回对象中 done 为 true 的时候,结束循环
Array.prototype[Symbol.iterator] = function values() {
    let self = this;
    let index = 0;

    return {
        next() {
            if (index > self.length - 1) {
                return {
                    done: true,
                    value: undefined
                }
            }

            return {
                done: false,
                value: self[index++]
            }
        }
    }
}

var arr = [10, 20, 30];

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

赋予for of遍历对象的能力

怎么能让 for of 遍历对象呢?

// 前置只是
//    @1 取对象 key 时,一定要注意包含对象自身的 Symbol key 值
//    @2 getOwnPropertySymbols 是 Object 的静态属性,不在原型链上
Object.prototype[Symbol.iterator] = function values() {
    let self = this;
    let keys = Object.keys(this).concat(Object.getOwnPropertySymbols(this));
    let index = 0;

    return {
        next() {
            if (index > keys.length - 1) {
                return {
                    done: true,
                    value: undefined
                }
            }

            return {
                done: false,
                value: self[keys[index++]]
            }
        }
    }
}

var obj = {
    name: 'ys',
    age: 10,
    0: 100,
    1: 200
};

for (let value of obj) {
    console.log(value); // 100  200 ys 10
}

赋予for of遍历类数组的能力

var obj = {
    0: 10,
    1: 20,
    2: 30,
    3: 40,
    length: 4
}

obj[Symbol.iterator] = Array.prototype[Symbol.iterator];

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