迭代器
例子
const arr = [1, 2, 3, 4];
for (x of arr) {
console.log(x);
}
console.log(arr[Symbol.iterator]); // ƒ values() { [native code] }
const iterator = arr[Symbol.iterator]();
for (let result = iterator.`next()`; !result.done; result = iterator.`next()`) {
console.log(result.value);
};
三个对象
- 可迭代对象: 在 JavaScript 中,可迭代对象就是继承了
Iterable
接口的对象,即拥有Symbol.iterator
作为键的属性。而这个属性必须引用一个迭代器工厂函数,调用这个工厂函数必须返回一个新迭代器 - 迭代器对象: 迭代器对象是一种一次性使用的对象,用于迭代与其关联的可迭代对象。使用
next()
方法在可迭代对象中遍历数据 - 迭代器结果对象: 迭代器结果对象就是迭代器返回的对象,是一个拥有
value
键与done
键的对象,迭代器对象每次调用next()
即返回迭代器结果对象,数据放在value
属性中,当迭代结束时,done
为true
迭代器对象也是可以迭代的
console.log(arr[Symbol.iterator]()[Symbol.iterator])
console.log(arr[Symbol.iterator]()[Symbol.iterator]()[Symbol.iterator])
console.log(arr[Symbol.iterator]()[Symbol.iterator]()[Symbol.iterator] == arr[Symbol.iterator]()[Symbol.iterator]) // true
自定义迭代器
class Counter {
constructor(n) {
this.n = n;
}
[Symbol.iterator]() {
let x = 0, n = this.n;
return {
next() {
return x > n ? { done: true } : {value: x++};
},
[Symbol.iterator]() {
return this;
}
}
}
}
let counter = new Counter(10);
const countNext = counter[Symbol.iterator]();
console.log(countNext); // {next: ƒ, Symbol(Symbol.iterator): ƒ}
const countResult = countNext.next();
console.log(countResult); // {value: 0}
for(x of counter) {
console.log(x)
}
我们在类中实现 Symbol.iterator
方法,并返回拥有 next()
方法的对象,则这就可以实例化出可迭代对象,并可以使用 for of
等进行遍历
其中, counter
就是可迭代对象, countNext
是迭代器对象, countResult
是迭代结果对象
提前终止迭代器
我们还可以给迭代器添加一个 return()
方法,用于将没有消费完但又不再需要的迭代器对象做一些操作,比如关闭文件等
如 for of
循环中如果使用 break
,就会自动调用迭代器的 return()
, return()
必须返回一个迭代器结果对象, done
为 true
class Counter {
constructor(n) {
this.n = n;
}
[Symbol.iterator]() {
let x = 0, n = this.n;
return {
next() {
return x > n ? { done: true } : {value: x++};
},
[Symbol.iterator]() {
return this;
},
return() {
console.log('return()');
return { done: true }
}
}
}
}
let counter = new Counter(10);
const iterator = counter[Symbol.iterator]();
for(x of iterator) {
console.log(x)
if (x === 4) break;
}
console.log("counter:", counter);
// 这里会继续打印上上面打印完的元素
for(x of iterator) {
console.log(x)
}
生成器
例子
function* generator() {
yield 35;
yield 'knight'
}
const gen = generator();
console.log(gen.next()); // {value: 35, done: false}
console.log(gen.next()); // {value: "knight", done: false}
console.log(gen.next()); // {value: undefined, done: true}
console.log("+++++++++++");
for (x of generator()) {
console.log(x) // 35 // knight
}
生成器是一个方法,只是在声明时需要在 function
之后,方法名之前加一个 *
号, *
号两边是否又空格都无所谓,如果使用简便写法,将 *
放在方法名前面即可
无法使用箭头函数声明生成器
yield
在生成器中,函数在执行之后回生成一个生成器对象,调用生成器对象的 next()
方法回开始执行函数,知道遇到 yield
关键字,则暂停执行,并返回生成器结果对象,当在此调用 next()
方法,则继续执行函数,知道返回 done: true
,生成器对象消费完之后如果继续调用 next()
方法,则返回 {value: undefined, done: true}
yield 关键字只能在生成器函数中使用,即带有 *
号的方法,如果在生成器中的普通函数或方法中使用也不可以,
class Counter {
constructor(n) {
this.n = n;
}
*[Symbol.iterator]() {
for (let i = 0; i < this.n; i++) {
yield i;
}
}
}
let counter = new Counter(10);
for(x of counter) {
console.log(x)
}
也可以直接使用生成器实现迭代器函数
yield*
yield*
可以迭代可迭代对象,然后会送得到的每一个值
function* generator(arr) {
yield* arr
}
const gen = generator('qweasdzxc');
for (const x of gen) {
console.log(x)
}
所以通过 yield*
我们可以递归我们的生成器
function* generator(n) {
if (n) {
yield* generator(n-1);
yield n-1;
}
}
const gen = generator(10);
for (const x of gen) {
console.log(x)
}
生成器返回值
function* generator() {
yield 35;
yield 'knight';
return 'over';
yield 'not';
}
const gen = generator();
console.log(gen.next()); // {value: 35, done: false}
console.log(gen.next()); // {value: "knight", done: false}
console.log(gen.next()); // {value: "over", done: true}
console.log(gen.next()); // {value: undefined, done: true}
如果我们在生成器中写 return
,那么返回值回作为迭代器结果对象的 value
值,只不过此时 done
为 true
,所以 for of
等会忽略这个 vaule
值
而在 return
之后的逻辑则不回执行
yield
和 next()
传值
yield
关键字是有返回值的,返回值就是调用 next()
时传进去的值
第一次调用 next()
时传的值无法获取到
function* generator() {
const innerFirst = yield 35;
console.log('innerFirst', innerFirst);
const innerSecond = yield 'knight';
console.log('innerSecond', innerSecond);
}
const gen = generator();
console.log('outFirst', gen.next('outFirst'));
console.log('outSecond', gen.next('outSecond'));
console.log('outThird', gen.next('outThird'));
提前终止生成器
return()
如果调用 return()
可以提前终止迭代器对象,如果迭代器对象未消费完也无法再进行消费
function* generator() {
yield 35;
yield 'knight';
yield 'coder';
}
const gen = generator();
console.log(gen.next()); // {value: 35, done: false}
console.log(gen.next()); // {value: "knight", done: false}
console.log(gen.return()); // {value: undefined, done: true}
console.log(gen.next()); // {value: undefined, done: true}
throw()
使用 throw()
可以想生成器抛一个错误
function* generator() {
const innerFirst = yield 35;
console.log('innerFirst', innerFirst);
const innerSecond = yield 'knight';
console.log('innerSecond', innerSecond);
const innerThird = yield 'coder';
console.log('innerThird', innerThird);
}
const gen = generator();
console.log(gen.next('outFirst'));
console.log(gen.next('outSecond'));
console.log(gen.throw('outThrow'));
console.log(gen.next('outThird'));
我们也可以使用 try catch
语句来处理这个错误
function* generator() {
try{
const innerFirst = yield 35;
console.log('innerFirst', innerFirst);
const innerSecond = yield 'knight';
console.log('innerSecond', innerSecond);
const innerThird = yield 'coder';
console.log('innerThird', innerThird);
} catch(e) {
console.log('catch', e)
}
}
const gen = generator();
console.log(gen.next('outFirst'));
console.log(gen.next('outSecond'));
console.log(gen.throw('outThrow'));
console.log(gen.next('outThird'));