迭代器和生成器
1.迭代器
1.1 迭代器对象
迭代器(iterator),是确使用户可在容器对象(container,例如链表或数组)上遍访的对象,设计人员使用此接口无需关心容器对象的内存分配的实现细节。其本质就是一个对象,符合迭代器协议(iterator protocol)
符合迭代器协议的对象,叫做迭代器对象
迭代器协议:
- 其对象包含一个next函数
- 调用next函数返回一个对象,其对象中包含两个属性:
- done:布尔值,指示当前迭代是否完成
- 如果这个迭代器没有迭代完成即返回
{done:false} - 当这个迭代器完成了即返回
{done:true}
- 如果这个迭代器没有迭代完成即返回
- value:(值),返回当前迭代后的结果,它可以返回js中的任何值,TS中表示可为:
value:any类型
- done:布尔值,指示当前迭代是否完成
以下是一个基本的迭代器
const arr = ['11', '22', '33']
let index = 0
const iterator = {
next() {
return {
value: index > arr.length ? undefined : arr[index++],
done: index > arr.length ? true : false,
}
},
}
console.log(iterator.next()) // {value: '11', done: false}
console.log(iterator.next()) // {value: '22', done: false}
console.log(iterator.next()) // {value: '33', done: false}
console.log(iterator.next()) // {value: undefined, done: true}以上代码符合迭代器的特点:是一个对象,包含next()函数且返回值为一个对象包含两个属性
但缺使用了全局变量,而且迭代器对象的源对象单一,只能应用于这一个数组
我们需要做如下封装
const arr = ['11', '22', '33']
const createIterator = arr => {
let index = 0
return {
next() {
return {
value: index > arr.length ? undefined : arr[index++],
done: index > arr.length ? true : false,
}
},
}
}
const it = createIterator(arr)
console.log(it.next()) // {value: '11', done: false}
console.log(it.next()) // {value: '22', done: false}
console.log(it.next()) // {value: '33', done: false}
console.log(it.next()) // {value: undefined, done: true}效果和第一个示例一样,但封装成了函数,可以创建多个迭代器对象,并且应用与多个数组,提升了可复用性
1.2 可迭代对象
迭代器对象和可迭代对象并不是一个东西,虽然它们存在关联
可迭代对象首先是一个对象,并且可以被迭代,符合可迭代对象协议(iterable protocol):
- 包含一个属性名为
[Symbol.iterator]的方法,且这个方法返回了一个迭代器对象
使用for...of遍历的时候,本质就是调用的这个函数,也就是[Symbol.iterator]为key的方法
1.2.1 JS内置可迭代对象
- String
- Array
- Set
- NodeList 类数组对象
- Arguments 类数组对象
- Map
控制台打印这些对象的原型可以发现一个Symbol(Symbol.iterator)方法,我们也可以直接使用
const arr = ['11', '22', '33']
const it = arr[Symbol.iterator]()
console.log(it.next()) // {value: '11', done: false}
console.log(it.next()) // {value: '22', done: false}
console.log(it.next()) // {value: '33', done: false}
console.log(it.next()) // {value: undefined, done: true}使用map的迭代器
const map = new Map([
['a', 111],
['b', 222],
['c', 333],
])
const it = map[Symbol.iterator]()
console.log(it.next()) // {value: ['a', 111], done: false}
console.log(it.next()) // {value: ['b', 222], done: false}
console.log(it.next()) // {value: ['c', 333], done: false}
console.log(it.next()) // {value: undefined, done: true}1.2.2 实现可迭代对象
js中对象(object)是没有迭代器的,所以不可迭代,但我们可以自己实现
const obj = {
a: 111,
b: 222,
c: 333,
[Symbol.iterator]() {
const arr = Object.keys(this)
let index = 0
const that = this
return {
next() {
return {
value: index > arr.length ? undefined : that[arr[index++]],
done: index > arr.length ? true : false,
}
},
}
},
}
// 使用迭代器对象
const it = obj[Symbol.iterator]()
console.log(it.next()) // {value: 111, done: false}
console.log(it.next()) // {value: 222, done: false}
console.log(it.next()) // {value: 333, done: false}
console.log(it.next()) // {value: undefined, done: true}
// 使用数组结构
const [a, b, c] = obj
console.log(a, b, c)
// 使用for..of循环
for (const key of obj) {
console.log(key)
}以上代码通过Object.keys()把对象的键存在数组中,每次迭代通过index获取数组中的键,从而获取obj的值
注意:next()函数中的this需要单独存一份,或者把next写成箭头函数
以上写法可以简化为以下写法
const obj = {
a: 111,
b: 222,
c: 333,
[Symbol.iterator]() {
return Object.values(this)[Symbol.iterator]()
},
}
// 使用迭代器对象
const it = obj[Symbol.iterator]()
console.log(it.next()) // {value: 111, done: false}
console.log(it.next()) // {value: 222, done: false}
console.log(it.next()) // {value: 333, done: false}
console.log(it.next()) // {value: undefined, done: true}
// 使用数组结构
const [a, b, c] = obj
console.log(a, b, c)
// 使用for..of循环
for (const key of obj) {
console.log(key)
}Object.values(this)返回一个包含对象所有值的数组,那么这个数组中已经有一个[Symbol.iterator]()函数,可以直接调用
2.生成器
生成器是ES6新增的一种可以对函数控制的方案,能灵活的控制函数的暂停执行,继续执行等。
生成器函数和普通函数的不同
- 定义: 普通函数
function定义,生成器函数function*,要在后面加* - 生成器函数可以通过
yield来控制函数的执行 - 生成器函数返回一个生成器(generator),生成器是一个特殊的迭代器
2.1 基本使用
生成器又叫做generator函数,是一种特殊类型的函数,generator函数使用function*修饰
function *fn(){
}TIP
箭头函数不能作为生成器
2.2 yield关键字
在生成器函数中可通过yield关键字返回一个值
生成器函数返回一个生成器对象,使用该对象的next()函数可以逐步获取 yield 的值。
function* fn() {
yield 1
yield 2
}
const suspend = fn()
console.log(suspend.next()) // {value: 1, done: false}
console.log(suspend.next()) // {value: 2, done: false}
console.log(suspend.next()) // {value: undefined, done: true}next()函数返回的对象有两个属性
- value:每次
yield返回的值,若已经没有yield的返回值,则为undefined - done:布尔类型,指示迭代是否完成
TIP
若迭代没有完成,每次调用next()函数都会暂停生成器的执行,再次调用next(),才会继续执行
2.3 yield*委托
yield* 是在生成器函数中使用的一种特殊语法,用于委托给另一个可迭代对象或生成器。使用 yield* 可以方便地从另一个生成器或可迭代对象中传递值,而不需要手动迭代它们。yield* 的基本语法如下:
function* generatorFunction() {
yield* anotherGenerator();
}在这个示例中,generatorFunction 会委托给 anotherGenerator(),并将其产生的所有值“传递”到调用 generatorFunction 的上下文中。
function* innerGenerator() {
yield 1;
yield 2;
}
function* outerGenerator() {
yield* innerGenerator(); // 委托给 innerGenerator
yield 3;
yield 4;
}
const gen = outerGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
console.log(gen.next().value); // 4
console.log(gen.next().value); // undefined在这个示例中,outerGenerator 使用 yield* 委托给 innerGenerator,因此在调用 outerGenerator 时,第一次返回的是 1 和 2,随后返回 3 和 4。
yield*可以 委托可迭代对象
function* arrayGenerator() {
yield* [111,]; // 委托给数组
}
const it = arrayGenerator();
console.log(it.next()) // {value: 111, done: false}
console.log(it.next()) // {value: 222, done: false}
console.log(it.next()) // {value: 333, done: false}
console.log(it.next()) // {value: undefined, done: true}2.4 生成器特性
生成器特性有如下特性
可暂停的执行:在调用
yield时,生成器会暂停,后续的调用会从暂停的地方恢复。可以传递值:可以在
next()方法中传递参数,这个值会成为yield表达式的返回值。例如:jsfunction* generator() { const value1 = yield '请给我一个值' console.log(value1) // 输出传入的值 } const gen = generator() console.log(gen.next().value) // 请给我一个值 gen.next(42) // 输出: 42完结状态:当生成器没有更多值可返回时,
done属性为true,且value为undefined。
