理解understanding es6 ---- 迭代器与生成器(iterators and generators)

英文电子书点此阅读《understanding es6》

目录

迭代器与生成器(iterators and generators)

iterator和generator的概念

iterator是具有特殊迭代接口的对象。它抽象了for loop等循环,统一用 .next() 方法来返回下一次迭代的结果。结果是一个包含 value 和 done 两个属性的对象。

generator用于生成iterator, 它需要在 function 关键字和 函数名字之前加一个 * 号。使用yield 关键字来决定 iterator 每次 next 后的对象的 value。 在一次yield后,函数被暂停。直到 iterator 的 next 方法被再次调用。

** yield 关键字 只能用于 generator 函数中 **
** 箭头函数不能同时为 generator 函数**

function *createIterator(items) {
    for (let i = 0; i < items.length; i++) {
        yield items[i];
    }
}

let 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 }"

// generator被用于对象内部的method

let o = {
    *createInterator(items){
     ...
    }
}

iterable 和 for-of

iterable 是 具有 Symbol.iterator 属性的对象。Symbol.iterator 这个特殊的常见 Symbol 为给定的对象返回 iterator。所有集合类的对象,如 array, set 和 map,以及字符串都有内置的iterator。它们都可以用 for-of 循环来遍历。

  • for-of 会直接返回 iterator.next() 的结果中的 value 值。
  • 不能在 非遍历对象、null、undefined 上使用 for-of。
  • 检测 iterator
function isIterable(object) {
    return typeof object[Symbol.iterator] === "function";
}
  • 普通对象Object不是iterable,但可以通过添加 Symbol.iterator属性,让它作为generator,来返回 iterator,从而可以进行 for-of 遍历。
let collection = {
    items: [],
    *[Symbol.iterator](){
        for(let item of this.items){   //注意有this
            yield item
        }
    }
}

collection.items.push(1,2,3)

for(let x of collection){
    console.log(x)
}

内置的迭代器

  • 对集合类的对象 array, map, set都有内置的iterator

    • entries() 返回一个value为key-value pair 的 iterator, [key,value]
    • values() 返回iterator,值为value
    • keys() 返回iterator,值为key,对数组只会取数字型的值。
  • array 和 sets 以values()方法返回默认的Iterator, 而 map 以 entries() 返回。[key,value]

  • 用 destruction 来直接获取 map 的 key 和 value
let data = new Map()

data.set('title','understanding es6')

for(let [key,value] of data){

    console.log(`${key} = ${value}`)
}
  • 字符串中的iterators:用string[i]获取的是code 单位,而es6致力于全面支持 Unicode, 用 for of 获取的是 character 单位。

  • HTML 细则 规定了 nodelist 也有默认的内置Iterator,可以用 for-of 来循环。类似于数组。

  • spread operator的应用

let smallNum = [1,2,3], bigNum = [101,102,103]
let allNum = [0,...smallNum,...bigNum]

console.log(allNum)   ///[0,1,2,3,101,102,103]

高阶用法

  • next()里传入的值会在generator作为上一次yield 的结果
  • 每一次generator的 迭代会 以 yield 关键词为界限。第n次会运行到nth yield 之前。
  • iterator 可以用 throw 方法来 抛出错误。

function *createIterator(){
    let first = yield 1
    let second = yield first + 2
    yield second + 3
}

let iterator = createIterator()

iterator.next()  //{value:1, done:false}
iterator.next(3) // {value:6, done:false}
iterator.next(5) // {value:8, done: false}

//如果是
iterator.next()  // {value:undefined, done:true}

// 如果是抛出错误,就像真的程序出错一样。
// 可以在generator内部用try catch来控制,此时仍然像next一样,因为code仍然会执行到下一次的 yield 并返回value

iterator.throw(new Error('boom'))  //Uncaught Error: boom 
  • generator 中的 return 同时做了两件事情,返回了yield 的值(就是 return 的值),和退出程序(将 done 设为 true)

  • delegating generators 事件委派

function *createNumberIterator() {
    yield 1;
    yield 2;
}

function *createColorIterator() {
    yield "red";
    yield "green";
}

function *createCombinedIterator() {
    yield *createNumberIterator();
    yield *createColorIterator();
    yield true;
}

var iterator = createCombinedIterator();

console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: 2, done: false }"
console.log(iterator.next());           // "{ value: "red", done: false }"
console.log(iterator.next());           // "{ value: "green", done: false }"
console.log(iterator.next());           // "{ value: true, done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"
  • createCombinedIterator中会将别的iterator 遍历到 done:true 之前,然后直接去遍历下一个。
  • return 可以用在iterator的委派中。作为最终的值。

注意以下CombinedIterator中的区别

function *createNumberIterator() {
    yield 1;
    yield 2;
    return 3;
}

function *createRepeatingIterator(count) {
    for (let i=0; i < count; i++) {
        yield "repeat";
    }
}

function *createCombinedIterator() {
    let result = yield *createNumberIterator();

    //yield result 如果有这一行,就会把this generator内部存的 numberiterator的最终value(return 3)也 output 出来

    yield *createRepeatingIterator(result);
}

var iterator = createCombinedIterator();

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: "repeat", 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 }"

执行异步任务

  • 利用yield 的暂停属性
function run(taskDef) {

    // create the iterator, make available elsewhere
    let task = taskDef();

    // start the task
    let result = task.next();

    // recursive function to keep calling next()
    function step() {

        // if there's more to do
        if (!result.done) {
            result = task.next(result.value);  // 把result.value 传给 next 作为参数,在yield 之间传值。这一步稍作改动就是异步了。
            step();
        }
    }

    // start the process
    step();

}

//使用回调
function run(taskDef) {

    // create the iterator, make available elsewhere
    let task = taskDef();

    // start the task
    let result = task.next();

    // recursive function to keep calling next()
    function step() {

        // if there's more to do
        if (!result.done) {
            if (typeof result.value === "function") {
                result.value(function(err, data) {
                    if (err) {
                        result = task.throw(err);
                        return;
                    }
                    // 把回调中的data作为上一次yield的结果传回去,成为contents 
                    result = task.next(data);  
                    step();
                });
            } else {                            // 这个else 语句其实是不会执行的

                setTimeout( () => {result = task.next(result.value)},0) // 确保线程空闲,上一步执行完   
                step();
            }

        }
    }

    // start the process
    step();

}

let fs = require("fs");

function readFile(filename) {
    return function(callback) {
        fs.readFile(filename, callback);
    };
}

run(function*() {
    let contents = yield readFile("config.json");
    doSomethingWith(contents);
    console.log("Done");
});

summary

  • Symbol.iterator 是对象中内置的 iterator。当一个对象有 Symbol.iterator 方法时,这个对象被称为 iterable
  • for-of 循环 适用于 array, set, map, 和 string。map 返回的是[key,value],其余返回的是 value。
  • generator 用于生成 iterator, 在函数名前有个*,用 yield 关键词定义当next()被调用时返回的值。
  • generator delegation 类似于事件委托,使用 yield* 而不是 yield,一个套一个。
  • 利用 yield 的停顿来化异步为同步,而不是使用 回调函数。

猜你喜欢

转载自blog.csdn.net/github_36487770/article/details/79710564