JavaScript03 --- 迭代器与生成器

迭代器

迭代:按照顺序反复多次执行一段程序,通常会有明确的终止条件。

计数循环是最简单的迭代:

function test() {
    for(let i = 0; i < 5; ++i){
        console.log(i);
    }
}
test();

最常见的迭代就是数组,因为数组有已知的长度,且数组每一项都可以通过索引获取,所以整个数组可以通过递增索引来遍历。

function test() {
	let arr = [1, 2, 3];
    for(let i = 0; i < arr.length; ++i){
        console.log(arr[i]);
    }
}
test();

但是由于以下原因,利用这种循环来执行例程并不理想:

​ (1)迭代之前需要事先知道如何使用数据结构。

​ (2)遍历顺序并不是数据结构固有的。通过递增索引来访问数据是特定于数组类型的方式,并不适用于其他具有隐式顺序的数据结 构。

迭代器是按需创建的一次性对象。每个迭代器都会关联一个可迭代对象,而迭代器会暴露迭代其关联可迭代对象的API。迭代器无需了解与其关联的可迭代对象的结构,只需要知道如何取得连续的值。

实现Iterator接口要求同时具备两种能力:支持迭代的自我识别能力和创建实现Iterator接口的对象能力。这意味着必须暴露一个属性作为默认迭代器,而且必须使用特殊的Symbol.iterator作为键。

function test() {
    // 没有实现迭代器工厂函数
    let num = 1;
    console.log(num[Symbol.iterator]); // undefined
    let obj = {};
    console.log(obj[Symbol.iterator]); // undefined
    // 实现了迭代器工厂函数
    let string = "abc";
    console.log(string[Symbol.iterator]); // ƒ [Symbol.iterator]() { [native code] }
    let arr = [1, 2, 3];
    console.log(arr[Symbol.iterator]); // ƒ values() { [native code] }
    let map = new Map().set("a", 1).set("b", 2).set("c", 3);
    console.log(map[Symbol.iterator]); // ƒ entries() { [native code] }
    let set = new Set().add(1).add(2).add(3);
    console.log(set[Symbol.iterator]); // ƒ values() { [native code] }
    let element = document.querySelectorAll("div");
    console.log(element[Symbol.iterator]); // ƒ values() { [native code] }
    // 调用工厂函数会生成一个迭代器
    console.log(string[Symbol.iterator]()); // StringIterator {}
    console.log(arr[Symbol.iterator]()); // Array Iterator {}
    console.log(map[Symbol.iterator]()); // MapIterator {'a' => 1, 'b' => 2, 'c' => 3}
    console.log(set[Symbol.iterator]()); // SetIterator {1, 2, 3}
    console.log(element[Symbol.iterator]()); // Array Iterator {}
}
test();

实际写代码的过程中,不需要显示调用这个工厂函数来生成迭代器。实现可迭代协议的所有类型都会自动兼容接收可迭代对象的任何语言特性。

function test() {
    let arr = ["a", "b", "c"];
    // for-of循环
    for(let x of arr){
        console.log(x); // a b c
    }
    // 数组解构
    let [a, b, c] = arr;
    console.log(a, b, c); // a b c
    //扩展操作符
    let arr2 = [...arr];
    console.log(arr2); // ['a', 'b', 'c']
    // Array.from()
    let arr3 = Array.from(arr);
    console.log(arr3); // ['a', 'b', 'c']
    // Set 构造函数
    let set = new Set(arr);
    console.log(set); // {'a', 'b', 'c'}
    // Map构造函数
    let map = arr.map((x, i) => [x, i]);
    console.log(map); // [['a', 1], ['b', 2], ['c', 3]]
    let map2 = new Map(map);
    console.log(map2); // {'a' => 0, 'b' => 1, 'c' => 2}
}
test();

next()方法返回的迭代器对象有两个属性:done和value。done是一个布尔值,表示是否可以调用next()取得下一个值,value包含可迭代对象的下一个值(done为false)或undefined(done为true)。

function test() {
    let arr = ["a", "b", "c"];
    let iter = arr[Symbol.iterator]();
    console.log(iter.next()); // {value: 'a', done: false}
    console.log(iter.next()); // {value: 'b', done: false}
    console.log(iter.next()); // {value: 'c', done: false}
    console.log(iter.next()); // {value: undefined, done: true}
}
test();

自定义迭代器:

class Counter{
    constructor(limit){
        this.limit = limit;
    }
    [Symbol.iterator](){
        let count = 1;
        let limit = this.limit;
        return {
            next() {
                if(count <= limit){
                    return { done: false, value: count++ };
                }
                else{
                    return { done: true, value: undefined };
                }
            }
        }
    }
}
let counter = new Counter(3);
for(let i of counter){
    console.log(i); // 1 2 3
}
for(let i of counter){
    console.log(i); // 1 2 3
}

每个以这种方式创建的迭代器也实现了Iterable接口。Symbol.iterator属性引用的工厂函数会返回相同的迭代器。

let arr = ['a', 'b', 'c'];
let iter1 = arr[Symbol.iterator]();
let iter2 = iter1[Symbol.iterator]();
console.log(iter1 === iter2); // true

因为每个迭代器也实现了Iterable接口,所以他们可以用在任何可迭代对象的地方。

let arr = [3, 1, 4];
let iter = arr[Symbol.iterator]();
for(let item of arr){
    console.log(item); // 3 1 4
}
for(let item of iter){
    console.log(item); // 3 1 4
}

生成器

生成器是一个极为灵活的结构,拥有在一个函数块内暂停和恢复代码执行的能力。可以使用生成器自定义迭代器和实现协程。

生成器的形式是一个函数,函数名称前面加一个(*)表示为一个生成器。

// 生成器函数说明
function *test() {}
注意:箭头函数不能用来定义生成器函数

调用生成器函数会产生一个生成器对象。生成器对象一开始处于暂停执行的状态。与迭代器相似,生成器对象也实现了Iterator接口,因此具有next()方法。调用这个方法会让生成器开始或恢复执行。

function *test(){}
const t = test();
console.log(t); // test {<suspended>}
console.log(t.next); // f next() { [native code] }
console.log(t.next()); // {value: undefined, done: true}

yield关键字可以让生成器停止和开始执行。生成器函数在遇到yield关键字之前会正常执行。遇到yield关键字后,执行会停止,函数作用域的状态会被保留。停止执行的生成器函数只能通过在生成器对象上调用next()方法来恢复执行。

function *test(){
    yield 'a';
    yield 'b';
    return 'c';
}
let x = test();
console.log(x.next()); // {value: 'a', done: false}
console.log(x.next()); // {value: 'b', done: false}
console.log(x.next()); // {value: 'c', done: true}

生成器对象可作为可迭代对象

function *test(){
    yield 'a';
    yield 'b';
    yield 'c';
}
for(let x of test()){
    console.log(x); // a b c
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值