Iterator迭代器和for/of循环原理
Iterator迭代器规范
自己创建一个Iterator类,来实现ES6中的迭代器规范:
class Iterator {
constructor(assemble) {
// assemble:需要迭代的数据结构
this.assemble = assemble
// index:记录迭代的次数(或者索引)
this.index = -1
}
// 必须具备next方法
next() {
this.index++
let {
assemble, index } = this
if (index >= assemble.length) {
// 迭代完毕
return {
done: true,
value: undefined
}
}
return {
done: false,
value: assemble[index]
}
}
}
创建一个实例对象,其应该符合迭代器规范的要求:
- itor.next() 具备next方法,执行这个方法可以依次遍历数据结构中的每一个成员
- 每一次遍历返回的结果是一个对象
{done:false,value:xxx}
- done: 是否迭代完毕
- value: 当前获取的成员值
符合以上两个特点的对象,我们称之为符合迭代器规范的对象
let arr = [10, 20, 30, 40],
itor = new Iterator(arr)
console.log(itor.next()) // {done: false, value: 10}
console.log(itor.next()) // {done: false, value: 20}
console.log(itor.next()) // {done: false, value: 30}
console.log(itor.next()) // {done: false, value: 40}
console.log(itor.next()) // {done: true, value: undefined}
在JS中,有很多数据结构,天生具备迭代器规范,例如:
我们主要看数据结构(对象)是否具备 Symbol.iterator 这个属性;有这个属性就具备迭代器规范,没有就不具备;具备这个规范,就可以使用 for/of 循环来迭代数据中的每一项值了。
- 数组 Array.prototype[Symbol(Symbol.iterator)]=function…
- 部分类数组:
- arguments[Symbol(Symbol.iterator)]
- NodeList.prototype[Symbol(Symbol.iterator)]
document.querySelectorAll('*')
- HTMLCollection.prototype[Symbol(Symbol.iterator)]
document.getElementsByTagName('*')
- …
- 字符串 String.prototype[Symbol(Symbol.iterator)]
- Set/Map
- …
但是对于纯粹对象「或者自己构建的类数组对象(以数字作为索引,索引从0开始逐级递增,有length属性表示长度)」等来讲,默认是不具备 Symbol.iterator 这个属性的,所以他们不具备迭代器规范「不能直接使用 for/of 循环」
for/of循环原理
重写数组迭代器规范(数组有内置的迭代器规范)来说明原理:
// 数组迭代的方式 for、while、forEach/map、for/in、for/of...
let array = [10, 20, 30, 40]
array[Symbol.iterator] = function () {
console.log('for/of Start');
let self = this,// this->array
index = -1
// 返回具备迭代器规范的对象->itorr
return {
next() {
index += 2
if (index >= self.length) {
return {
done: true,
value: undefined
}
}
return {
done: false,
value: self[index]
}
}
}
}
// let itorr = array[Symbol.iterator]()
for (let val of array) {
console.log(val);
}
// for/of循环主要用于获取数据结构中每一项的‘值’
for (let val of array) {
console.log(val);
}
原理:
1.迭代执行,先执行数组的 Symbol.iterator 这个方法,获取一个具备迭代器规范的对象 -> itor
2.开始迭代:每一次迭代都是把 itor.next 方法执行
- 把获取对象中的value属性值,赋值给val这个变量
- 再看对象中done这个属性的值,如果是false,则继续迭代;如果是true,则结束迭代
普通对象是不具备迭代器规范的:
let obj = {
name: '52lkk',
age: 24,
0: 100,
[Symbol('AA')