Iterator迭代器(遍历器)
前言
平时的开发语言基本上都会用到迭代器,有的叫遍历器,有的叫枚举,都是一个意思,就是将我们的集合或者特定的结构,通过遍历器能够访问到其允许访问的所有成员,这就是遍历、迭代器了
本篇主要讲的是 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 的基础功能,一些注意不到的细节罢了