前端问题清单——JavaScript的迭代器

134 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

迭代器

迭代器实现对不同结构的容器对象的遍历,开发者不用关心容器对象的内存分配的实现细节。简单来说,在使用迭代器的时候,可以在不用关心被遍历的对象是怎么实现的情况下遍历对象。

JavaScript的迭代器

JavaScript内的迭代器都应该遵循迭代协议。

迭代协议

迭代协议作为ECMA2015的补充规范,不是新语法,用来约束行为。迭代协议分为可迭代协议和迭代器协议。

可迭代协议

可迭代协议协议允许JavaScript对象定义或者定制它们的迭代行为。

要成为可迭代对象,这个对象必须实现@@iterator方法。StringArrayMapSet这些内置对象的原型对象都实现了@@iterator方法,有默认的迭代行为。其他的对象可以通过实现@@iterator方法定制迭代行为,那这个对象或者说原型链上的某个对象会有一个键为@@iterator的属性,可以通过Symbol.iterator访问这个属性。

值得一提的是ES6之后新增的for-of。当使用for-of迭代一个对象时,会先调用它的@@iterator方法,通过这个方法返回的迭代器来获得要迭代的值。

迭代器协议

定义了一个对象要成为迭代器需要的next()方法。

next()必须返回一个对象,对象要有两个属性:donevalue,如果没有返回对象,会抛出TypeError错误。

done:如果迭代器可以生成序列中的下一个值,为false,如果迭代完毕,为true。当donetruevalue可以省略,但是如果value存在,value是迭代结束后的默认返回值。

value:迭代器返回的任何JavaScript值,donetrue的时候可省略。

// 举个栗子:自定义iterator
let obj = {
  [Symbol.iterator]:()=>({
    text: 'object',
    next(){
        if(this.text==='test'){
            return {
                done: true
            }
        } else {
            return{
                value: this.text = 'test',
                done: false
            }
        }
    }
  })
}
for(let i of obj){
    console.log(i)
}

// 输出是test

在迭代器的角度看循环

do-while

do{
语句1
}while(语句2)

先执行语句1,再执行语句2,如果语句2返回true,继续执行语句1,如果语句2返回false,停止循环。

语句2返回true/false,可以理解next()返回done的值,语句1的执行结果是next()value

while

while(语句1){
语句2
}

先执行语句1,如果返回true,继续执行语句1,如果语句1返回false,停止循环。

语句1返回的true/false,可以理解next()返回done的值,语句2的执行结果是next()value

for

for(语句1;语句2;语句3) {
    语句4
}

先执行语句1,再执行语句2,如果返回true,执行语句4,然后执行语句3,拿到语句3的结果后,再执行语句2,当语句2返回false时,停止循环。

可以理解在语句2返回的true/falsenext()返回的done值,语句4执行的结果是next()value

for-in

for(x in obj) {
    语句
}

JavaScript特有循环对象的可枚举属性,体现出对象属性的enumerable

可以理解属性是否是可枚举属性是next()done值,读取到的属性值为next()value

for-of

for-in类似,前文已经介绍过。

for-await-of

for-of类似,不同的是,next()value是一个异步函数。

结语

JavaScript迭代器也有一些讨论,比如关于迭代器和生成器的讨论。限于篇幅,先写一些比较清晰的结论。欢迎留言讨论。