JavaScript 内核第 5 / 7 章
迭代器与生成器
理解迭代协议,以及生成器如何按需暂停和恢复。
生成器迭代器
迭代器
迭代器
🎁 提示
使用户可以在容器对象上遍访的对象,使用该接口无需关心对象内部的实现细节 在js中,迭代器是一个具体的对象,这个对象需要符合迭代器协议:
- 迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式
- js中,这个标准就是实现了一个特定的next方法 next方法的要求:
- 有一个参数或无参数
- 必须返回一个包含value和done的对象。done是一个boolean,表示是否迭代完成,value表示当前具体值
const arr = [1,2,3,4]
let index = 0
const arrIterator = {
next:function(){
if(index<arr.length){
return {
done:false,
value: arr[index++]
}
}else {
return {
done:true,
}
}
}
}
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
可迭代对象
❤️ 提示
可迭代对象
- 必须实现了[Symbol.iterator]函数
- [Symbol.iterator]函数必须返回一个迭代当前对象的迭代器 可迭代对象的特性:
- js语法:
for...of操作、展开运算符操作、yield、解构赋值- 创建对象时:new Set(iterable)、new Map()、new weakMap() 、new weakSet()
- 常见的方法如:Promise.all(iterable)、Promise.race、promise.any,Array.from(iterable)
在构建对象字面量时,也可以使用展开运算符,这个是在ES2018(ES9)中添加的新特性
所以不是在构建字面量对象的时候,对一个对象使用展开运算符,会报错
const info = { name:'info',age:18}
function foo(...arg){
console.log('arg', arg)
}
foo(...info) //报错
迭代器中断
在迭代过程中被break、return、throw中断,在迭代器中监听中断事件,在迭代器中实现return方法
const arr = [1,2,3,4]
let index = 0
const arrIterator = {
next:function(){
if(index<arr.length){
return {
done:false,
value: arr[index++]
}
}else {
return {
done:true,
}
}
}
return : ()=>{
console.log('被中断了')
return {done:true}
}
}
原生可迭代对象
Array、Set、Map、String、arguments对象、NodeList等
生成器
🏝️ 提示
生成器是一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等,ES6新增 在js中,生成器是指由生成器函数返回的对象,它同时也一个特殊的迭代器,实现了迭代器协议 (即next方法),也是一个可迭代对象,实现可迭代协议
生成器函数
生成器函数也是一个函数,但与普通函数有一些区别:
生成器函数需要在function后面加符号: *
生成器函数可以通过yield关键词来控制函数的执行流程(普通函数不可以)
生成器函数的返回值是一个Generator(生成器):
- 生成器事实上是一种特殊的迭代器
function* foo(init){
console.log('内部代码执行: 1')
const res = yield init
console.log('内部代码执行: 2')
const res2 = yield init+ res
console.log('内部代码执行: 3')
const res3 = yield init + res2
console.log('内部代码执行: 4')
const res4 = yield init + res3
console.log('内部代码执行: 5')
yield init + res4
console.log('内部代码执行: 6')
}
const generator = foo(1)
console.log('generator.next(1).value', generator.next().value)
console.log('generator.next(2).value', generator.next(2).value)
console.log('generator.next(3).value', generator.next(3).value)
console.log('generator.next(4).value', generator.next(4).value)
console.log('generator.next(5).value', generator.next(5).value)
console.log('generator.next(6).value', generator.next(6).value)
//内部代码执行: 1
//generator.next(1).value 1
//内部代码执行: 2
//generator.next(2).value 3
//内部代码执行: 3
//generator.next(3).value 4
//内部代码执行: 4
//generator.next(4).value 5
//内部代码执行: 5
//generator.next(5).value 6
//内部代码执行: 6
//generator.next(6).value undefined
📍 提示
- 执行生成器函数返回一个生成器,但不会执行函数内部任何代码
- 第一次调用生成器的next方法,会执行函数体第一行到第一个yield之间的代码,并可以通过yield返回值,后面每次调用next方法,都会执行上一个yield后面都下一个yield或者return之间的语句
- next可以传递参数,在函数中通过yield前面定义的变量接收
- 函数中return或者生成器调用return方法都可以终止生成器函数的继续执行
- 生成器的return方法也可以传递参数,也可以使用throw方法传递一个错误来提前终结
使用生成器
🎨 提示
场景:
- 自定义的迭代器太麻烦了,需要自己实现,生成器函数可以简化迭代器的生成 yield* 语法糖 yield* arr arr需要是一个可迭代对象,那么会自动迭代arr返回
const arr = [1,2,34,4]
function* foo(){
yield* arr
}
const generator = foo()
console.log('generator.next()', generator.next()) // {value: 1}
console.log('generator.next()', generator.next()) // {value: 2}
console.log('generator.next()', generator.next()) // {value: 34}
console.log('generator.next()', generator.next()) // {value: 4}
console.log('generator.next()', generator.next()) // {done: true}
总结:
🥛 提示
生成器函数相当于可以替代原来[Symbol.iterator]函数,快速实现可迭代协议
const b = {
friends:[1,2,3,4],
[Symbol.iterator](){
let index = 0
return {
next:()=>{
if(index<this.friends.length){
return {
value:this.friends[index++],
done:false
}
}
else {
return {
done:true
}
}
}
}
}
}
for (const iterator of b) {
console.log('iterator', iterator)
}
//改造后
const b = {
friends:[1,2,3,4],
*[Symbol.iterator](){
yield* this.friends
}
}
for (const iterator of b) {
console.log('iterator', iterator)
}