迭代器
什么是迭代器
实现了 next 方法的对象就是迭代器。这个 next 方法可以有零个或者一个参数,返回一个对象,这个对象的格式见如下示例代码:
const iter = {
next() {
return {done: false, value: 1}
}
}
done
表示迭代是否完成,value
表示迭代的值。假设我们创建一个数组的迭代器,那么 done: false
的情况发生在当前遍历的下标大于等于数组的长度时,此时表示迭代结束,value
的值为 undefined
。
使用迭代器
console.log(iter.next()); // {done: false, value: 1}
console.log(iter.next()); // ..同上
console.log(iter.next());
console.log(iter.next());
可迭代对象
实现了 @@iterator
方法的对象被称为可迭代对象,在代码中我们用 Symbol.iterator
代替 @@iterator
作为方法的名字。@@iterator
方法返回的是一个迭代器。
const iterable = {
names: ['jack', 'tom', 'lily'],
[Symbol.iterator]: function() {
let idx = 0;
return {
next: () => {
return {
done: idx >= this.names.length,
value: this.names[idx++]
};
}
}
}
}
// 获取迭代器
const iterator = iterable[Symbol.iterator]();
console.log(iterator.next());
需要注意的是,每次调用 @@iterator
方法的时候,都会返回一个新的迭代器。
同一个迭代器被迭代完后(也就是 done: true
的时候),无论进行多少次迭代都不会改变该迭代器的状态,所以可迭代对象每次返回新的迭代器是为了避免对对象迭代一次后无法再次迭代该对象。可以参考 for ... of ...
只能迭代一个可迭代对象的例子。
生成器
生成器实际是一种特殊的迭代器,调用生成器函数会返回一个迭代器,通过这个迭代器的 next
方法执行生成的代码。Python 有类似的功能,写过的肯定不会陌生。
function* foo() {
for(let i=0; i<=10; i++) {
yield i
}
}
const generator = foo();
generator.next() // 0
// ... 继续调用8 次
generator.next() // 10
定义生成器的方法是 function
关键字后加一个 *
,即 function* 函数名
。
前面说过生成器是一种特殊的迭代器,所以如果打印生成器的返回值,会发现返回的是迭代器格式的对象 (类似{done: false, value: xxx}
)。
function* MyGenerator() {
yield 'yield的含义是:执行此处时,暂停执行当前函数'
yield '暂停之后的函数可以用next方法继续执行'
return '遇到return之后会真正结束,done会变成true'
}
const gen = MyGenerator() //执行后返回的是一个指针,可以利用该指针执行next方法
console.log(gen.next()) // {value: "yield的含义是:执行此处时,暂停执行当前函数“, done: false}
console.log(gen.next())// {value: "暂停之后的函数可以用next方法继续执行", done: false}
console.log(gen.next())// {value: "遇到return之后会真正结束,done会变成true", done: true}
可以用 return
终止生成器的执行,函数的返回值在 .value
中。
yield 的返回值
yield 的返回值是下次 next 函数中传入的值。注意,yield
表达式执行完后函数会立即停止执行。
function* gen() {
const n = yield 10;
console.log('I am here.')
return n + 10;
}
const g = gen()
g.next();
g.next(10) // I am here. \n 20
上面 const n = yield 10
,n 在第二次执行 next
的时候才会被赋值。
yield 可迭代对象*
用 yield*
可以直接生产可迭代对象,功能类似遍历可迭代对象取出 item 然后 yield item
function* gen() {
for(const name of names) {
yield name;
}
}
function* gen1() {
yield* names;
}
上面 gen
和 gen1
的代码是等价的。