2025-01-09 日报 Day61

2025-01-09 日报 Day61

Yuyang 前端小白🥬

今日的鸡汤

只有付出比别人更多的努力和功夫,你才能在一次次磨砺中,变得闪闪发光。

今日学习内容

1、JS 红皮书 P201-204 第七章:迭代器与生成器

今日笔记

1、生成器作为默认迭代器: 因为生成器对象实现了 Iterable 接口,而且生成器函数和默认迭代器被调用之后都产生迭代器,所以生成器格外适合作为默认迭代器。下面是一个简单的例子,这个类的默认迭代器可以用一行代码产出类的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Foo { 
constructor() {
this.values = [1, 2, 3];
}
* [Symbol.iterator]() {
yield * this.values;
}
}
let foo = new Foo();
for (let value of foo) {
console.log(value);
}
// 1
// 2
// 3

2、提前终止生成器: 与迭代器类似,生成器也支持“可关闭”的概念。一个实现 Iterator 接口的对象一定有 next()方法,还有一个可选的 return()方法用于提前终止迭代器。生成器对象除了有这两个方法,还有第三个方法:throw()。

1
2
3
4
5
6
function* generatorFn() {} 
const g = generatorFn();
console.log(g); // generatorFn {<suspended>}
console.log(g.next); // f next() { [native code] }
console.log(g.return); // f return() { [native code] }
console.log(g.throw); // f throw() { [native code] }
  • return(): return()方法会强制生成器进入关闭状态。提供给 return()方法的值,就是终止迭代器对象的值:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function* generatorFn() { 
    for (const x of [1, 2, 3]) {
    yield x;
    }
    }
    const g = generatorFn();
    console.log(g); // generatorFn {<suspended>}
    console.log(g.return(4)); // { done: true, value: 4 }
    console.log(g); // generatorFn {<closed>}
    与迭代器不同,所有生成器对象都有 return()方法,只要通过它进入关闭状态,就无法恢复了。后续调用 next()会显示 done: true 状态,而提供的任何返回值都不会被存储或传播:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    function* generatorFn() { 
    for (const x of [1, 2, 3]) {
    yield x;
    }
    }
    const g = generatorFn();
    console.log(g.next()); // { done: false, value: 1 }
    console.log(g.return(4)); // { done: true, value: 4 }
    console.log(g.next()); // { done: true, value: undefined }
    console.log(g.next()); // { done: true, value: undefined }
    console.log(g.next()); // { done: true, value: undefined }
    // for-of 循环等内置语言结构会忽略状态为 done: true 的 IteratorObject 内部返回的值。
    function* generatorFn() {
    for (const x of [1, 2, 3]) {
    yield x;
    }
    }
    const g = generatorFn();
    for (const x of g) {
    if (x > 1) {
    g.return(4);
    }
    console.log(x);
    }
    // 1
    // 2
  • throw(): throw()方法会在暂停的时候将一个提供的错误注入到生成器对象中。如果错误未被处理,生成器就会关闭:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function* generatorFn() { 
    for (const x of [1, 2, 3]) {
    yield x;
    }
    }
    const g = generatorFn();
    console.log(g); // generatorFn {<suspended>}
    try {
    g.throw('foo');
    } catch (e) {
    console.log(e); // foo
    }
    console.log(g); // generatorFn {<closed>}
    不过,假如生成器函数内部处理了这个错误,那么生成器就不会关闭,而且还可以恢复执行。错误处理会跳过对应的 yield,因此在这个例子中会跳过一个值。比如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function* generatorFn() { 
    for (const x of [1, 2, 3]) {
    try {
    yield x;
    } catch(e) {}
    }
    }
    const g = generatorFn();
    console.log(g.next()); // { done: false, value: 1}
    g.throw('foo');
    console.log(g.next()); // { done: false, value: 3}
    3、小结: ECMAScript 6 正式支持迭代模式并引入了两个新的语言特性:迭代器和生成器。
    迭代器是一个可以由任意对象实现的接口,支持连续获取对象产出的每一个值。任何实现 Iterable接口的对象都有一个 Symbol.iterator 属性,这个属性引用默认迭代器。默认迭代器就像一个迭代器工厂,也就是一个函数,调用之后会产生一个实现 Iterator 接口的对象。
    迭代器必须通过连续调用 next()方法才能连续取得值,这个方法返回一个 IteratorObject。这个对象包含一个 done 属性和一个 value 属性。前者是一个布尔值,表示是否还有更多值可以访问;后者包含迭代器返回的当前值。这个接口可以通过手动反复调用 next()方法来消费,也可以通过原生消费者,比如 for-of 循环来自动消费。
    生成器是一种特殊的函数,调用之后会返回一个生成器对象。生成器对象实现了 Iterable 接口,因此可用在任何消费可迭代对象的地方。生成器的独特之处在于支持 yield 关键字,这个关键字能够暂停执行生成器函数。使用 yield 关键字还可以通过 next()方法接收输入和产生输出。在加上星号之后,yield 关键字可以将跟在它后面的可迭代对象序列化为一连串值。
此页目录
2025-01-09 日报 Day61