Iterator迭代器(遍历器)

295 阅读5分钟

前言

平时的开发语言基本上都会用到迭代器,有的叫遍历器,有的叫枚举,都是一个意思,就是将我们的集合或者特定的结构,通过遍历器能够访问到其允许访问的所有成员,这就是遍历、迭代器了

本篇主要讲的是 js 中的迭代器(遍历、枚举),其与 generator 函数也有着不小的关系,仔细一看就能感觉到 generator 方法就是 Iterator 迭代器 的另一个版本哈

前面有介绍过 generator,参考 Generator函数与async函数介绍

Iterator

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)

我们常见的支持遍历的对象有:Array、set、map等,甚至是 String 也提供了 Iterator遍历器 接口,方便我们遍历,一些结构还支持返回遍历器,方便遍历

遍历器常常伴随着 for ... of,其可以很好地访问我们的遍历器,而 forEach 等并不是遍历器标配,支持 Iterator 遍历的也不一定有 forEach 类似的方法哈,但是 for ... of 一定可以

尝试实现 Iterator

话不多说了,先来一组遍历器的使用

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;
    const len = array.length
    return {
        next: function () {
            return nextIndex < len
                ? { value: array[nextIndex++], done: false }
                : { value: undefined, done: true };
        },
    };
}

看到遍历器的实现,我们大概知道了遍历器的一些特征

  • 遍历器对象包含 next 方法
  • 每次遍历都会调用 next 方法,调用完毕后,指针会往后移,直到 len 时,返回固定的结果
  • 遍历器的 next 对象在没结束前返回 { value, done }结构,value 返回当前遍历的内容,done表示是否结束了,当结束时,done 返回 true

看了上面结构,我们可以稍微在简化一下 makeIterator,去掉多余的 done 和 value,也能够减少一部分内容

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

模仿 Iterator 遍历

模仿一下 for ... of 的遍历逻辑

var it = makeIterator(["a", "b"]);
let next = it.next()
while (!next.done) {
    //如果要封装,这里可以回调 item、index 等内容,index可以在枚举器返回,也可以这里记录
    console.log(next.value)
    next = it.next
}

[Symbol.iterator] 接口

Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名Symbol.iterator,它是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为 Symbol 的特殊值

下面我们给 arrayLike 对象实现一个遍历器吧,当然想更通用,可以直接创建一个类似 ArrayLike 类,实现原型函数即可,每次只需要new一个此类型的对象,就可以实现通用了

const arrayLike = { '0': 1, '1': 1, '2': 2,  length: 2 }
arrayLike[Symbol.iterator] = function() {
    let nextIndex = 0;
    let self = this
    const len = self.length;
    return {
        next: function () {
            return nextIndex < len
                ? { value: self['' + nextIndex++] }
                : { done: true };
        },
        return: function () {
            console.log(self[nextIndex])
            return {};
        }
    };
}

我们稍微改进一下,让其更通用,这样就 ok 了

class ArrayLike {
    constructor(obj) {
        this.obj = obj
    }
}

ArrayLike.prototype[Symbol.iterator] = function () {
    let nextIndex = 0;
    let self = this;
    const len = self.length;
    return {
        next: function () {
            return nextIndex < len
                ? { value: self["" + nextIndex++] }
                : { done: true };
        },
        return: function () {
            console.log(self[nextIndex]);
            return {};
        },
    };
};

//遍历一下发现成功了
const alike = new ArrayLike({ 0: 0, 1: 1, 2: 2, length: 2 });
for (const v of arrayLike) {
    console.log('arrayLike', v);
}

复用遍历器

我们发现 ArrayLike 和 Array 的遍历功能很相似,可以不用写,直接使用 Array 的遍历器即可

ArrayLike.prototype[Symbol.iterator]: Array.prototype[Symbol.iterator]

解构与遍历器

除了遍历,有时候我们也会使用 es6 语法解构、展开一些遍历器对象,此时就会自动调用迭代器

如果解构出错,有时候会报 Iterator 相关错误相信了解原因了

//集合解构也会默认调用 iterator
const set = new Set()
const list = [...set]
const [first, ...others] = list; //集合第一个给first,其他的给 others

遍历器 next、return、throw

遍历器除了 next,还有 return、throw 相关内容函数,调用 next 相当于 continue 功能,自动进入下一个,而 return 则相当于循环内的 break,throw 就不多说了 throw 一个异常了

return 和 throw 为一个 可选项,不实现也没啥影响,会自动使用默认功能,使用了也需要返回默认功能

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

实现一个 return,当for ... of 中出现 break 则会调用,我们需要返回一个对象结构(和next返回一样的结构),试了一下,返回一个{}, {done: false}都会正常 break,也就是这个方法监听break用的,平时基本不会用到, throw 也就不多介绍了

{
    next: function () {
        return nextIndex < len
            ? { value: self['' + nextIndex++] }
            : { done: true };
    },
    //我们可以通过 return 
    return: function () {
        console.log(self[nextIndex])
        return {done: true};
    }
};

最后

Iterator 和 generator 也挺像的,但就介绍到这里吧,想继续了解可以看 Generator函数与async函数介绍,本篇就不多介绍了,东西没多少,就是包含了 Iterator 的基础功能,一些注意不到的细节罢了