迭代器与生成器

Thursday, August 22, 2019

第八章 迭代器与生成器

迭代器

迭代器是一个特殊对象,每一个迭代器对象有 next()方法,返回一个包含 value 和 done 属性的对象。

function createIterator(items) {
    var i = 0;
    return {
        next: function() {
            var done = (i >= items.length);
            var value = !done ? items[i++] : undefined;
            return {
                done: done,
                value: value
            };
        }
    };
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: 2, done: false }"
console.log(iterator.next());           // "{ value: 3, done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"
// 之后的所有调用
console.log(iterator.next());           // "{ value: undefined, done: true }"

生成器

生成器是函数,用于返回迭代器,ES6 内部实现了迭代器功能,只需用 yield 来迭代输出。

function* createIterator() {
    yield 1
    yield 2
    yield 3
}
const a = createIterator()
console.log(a.next()) //{value: 1, done: false}
console.log(a.next()) //{value: 2, done: false}
console.log(a.next()) //{value: 3, done: false}
console.log(a.next()) //{value: undefined, done: true}

在 for 循环中使用 yield 关键字,可以暂停循环,但只能在生成函数内部(内部嵌套函数也不行)使用。

可以用函数表达式表示:

const createIterator = function* () {
    yield 1
    yield 2
}

也可以在对象内添加:

const obj = {
    a: 'hahaha',
    *createIterator() {
        yield this.a
    },
}
let b = obj.createIterator()
console.log(b.next()) // {value: "hahaha", done: false}

可迭代对象与 for-of 循环

可迭代对象指包含了Symbol.iterator属性的对象。在 ES6 中所有的集合(数组,Set,Map)以及字符串都是可迭代对象。

for-of 循环每次循环都会调用可迭代对象的next()方法,直至done=true,循环时将 value 属性值读出并放入 num 变量。

let values = [1, 2, 3];
for (let num of values) {
    console.log(num);      // 1 2 3      三行显示
 }

访问默认迭代器

let iterator = values[Symbol.iterator]();
console.log(iterator.next());

创建可迭代对象

let collection = {
    items: [],
    *[Symbol.iterator]() {
        for (let item of this.items) {
            yield item;
        }
    }
};
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
    console.log(x);
}

内置的迭代器

集合迭代器

数组,Map,Set 有entries(),values(),keys()迭代器

entries() 返回键和值(Map 默认迭代器) values()返回值(数组和 Set 默认迭代器) keys()返回键

for (let entry of map.values()) { // let entry of map  使用默认迭代器
    console.log(entry);
}

字符串迭代器

可以正确输出 unicode 字符,使用 for 会返回两个码元(无法识别)。

var message = "A 𠮷 B";
for (let c of message) {
    console.log(c);
}

NodeList 的迭代器

var divs = document.getElementsByTagName("div");
for (let div of divs) {
    console.log(div.id);
}

扩展运算符与非数组的可迭代对象

可迭代对象转换为数组的最简单方法。

let smallNumbers = [1, 2, 3],
    bigNumbers = [100, 101, 102],
    allNumbers = [0, ...smallNumbers, ...bigNumbers];

console.log(allNumbers.length);     // 7
console.log(allNumbers);    // [0, 1, 2, 3, 100, 101, 102]

迭代器高级功能

传递参数给迭代器

传递给next()的参数会成为上次生成器执行处yield 语句的值,因此第一次执行next()方法传入任何参数都是无效的。

function* create() {
    let first = yield 'hahah'
    let second = yield first + 2
}
const b = create()
console.log(b.next(88)) // {value: "hahah", done: false}
console.log(b.next(56)) // {value: 58, done: false}
console.log(b.next()) // {value: undefined, done: true}

Figure 8-1: Code execution inside a generator

在迭代器中抛出错误

function* create() {
    let first = yield 'hahah'
    let second
    try {
        yield first + 2
    } catch (ex) {
        second = 'xixixi'
    }
    yield second + 'LOL'
}
const b = create()
console.log(b.next(88)) // {value: "hahah", done: false}
console.log(b.next(56)) // {value: 58, done: false}
console.log(b.throw(new Error('yOOO'))) //{value: "xixixiLOL", done: true}

Figure 8-2: Throwing an error inside a generator

生成器的 Return 语句

使用return表示处理已完成,done会设置成true,如果提供了返回值,则被用于value

生成器委托

将多个迭代器的值合并一起使用。

function *createNumberIterator() {
    yield 1;
    return 2;
}
function *createRepeatingIterator(count) {
    for (let i=0; i < count; i++) {
        yield "repeat";
    }
}
function *createCombinedIterator() {
    let result = yield *createNumberIterator();
    yield *createRepeatingIterator(result);
}
var iterator = createCombinedIterator();
console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: "repeat", done: false }"
console.log(iterator.next());           // "{ value: "repeat", done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"

异部任务运行(未看)