es6之迭代器、生成器

迭代器(iterator)

简单来讲,迭代器(iterator)就是一个知足必定规则的对象。数组

规则:若是一个对象中含有一个next方法,next方法中返回一个对象,其中中包含两个属性valuedone,那么这个对象就是一个迭代器。浏览器

以下代码就是一个迭代器:dom

//生成随机数的迭代器
let iterator = {
    total: 2,
    i: 1,
    next(){
        let obj = {
            value: this.i > this.total ? undefined : Math.random(), //下一个数据的值
            done: this.i > this.total, //是否还有下一个数据
        }
        this.i++;
        return obj;
    }
}

执行方式,调用next函数异步

console.log(iterator.next()) //{value: 1, done: false}
console.log(iterator.next()) //{value: 2, done: false}
console.log(iterator.next()) //{value: undefined, done: true}

遍历迭代器数据函数

let next = iterator.next();

while(!next.done){
    console.log(next.value);
    next = iterator.next();
}

迭代器建立函数(iterator creator)

一个返回迭代器对象的函数。this

function createIterator(total = 2){
    let i = 1;
    return {
        next(){
            let obj = {
                value: i > total ? undefined : Math.random(), //下一个数据的值
                done: i > total, //是否还有下一个数据
            }
            i++;
            return obj;
        }
    }
}

const iterator = createIterator(5)

将一个数组改为迭代模式:code

function createArrIterator(arr){
    let i = 0;
    return {
        next(){
            let obj = {
                value: arr[i++],
                done: i > arr.length
            }
            return obj;
        }
    }
}

const iterator = createArrIterator([3, 4,5, 23, 4, 6, 34]);

迭代协议

只有知足迭代协议的对象才能使用for of遍历,不然就会报错。对象

const obj = {}

for(const i of obj){
    console.log(i) // "Uncaught TypeError: obj is not iterable"
}

若是想使用for of遍历obj,只要将obj改形成知足可迭代协议的对象便可。开发

迭代协议:知足可迭代协议的对象要有一个符号属性(Symbol.iterator),属性的值是一个无参的迭代器建立函数。以下所示:get

//obj加入[Symbol.iterator]属性后可被迭代
const obj = {
    [Symbol.iterator]: (total = 2) => { //迭代器建立函数
        let i = 1;
        return {
            next() {
                let obj = {
                    value: i > total ? undefined : Math.random(), //下一个数据的值
                    done: i > total, //是否还有下一个数据
                }
                i++;
                return obj;
            }
        }
    }
}

for(const i of obj){
    console.log(i); // 不报错
}
ArrayMapSet可使用 for of遍历,是由于他们的原型上都有符号属性 (Symbol.iterator)

for of执行原理

实际上for of遍历可迭代对象时,就是运行该对象上的Symbol.iterator属性,而后调用迭代器的next方法直到done属性是true位置。

const obj = {
    [Symbol.iterator]: (total = 2) => {
        let i = 1;
        return {
            next() {
                let obj = {
                    value: i > total ? undefined : Math.random(), //下一个数据的值
                    done: i > total, //是否还有下一个数据
                }
                i++;
                return obj;
            }
        }
    }
}


for(const i of obj){
    console.log(i); // 不报错, i实际上就是迭代器next方法返回对象里面的value属性
}

//for of 代码至关于以下代码:
const iterator = obj[Symbol.iterator]();
let item = iterator.next();
while(!item.done){
    const i = item.value;
    console.log(i) //执行for of中的代码
    item = iterator.next()
}

生成器 generator

由构造函数Generator建立的对象,建立的对象是一个迭代器,也知足可迭代协议。

Generator构造函数是浏览器内置的,开发者没法使用。
//伪代码
const generator = new Generator();

//generator的内部大概是这样的:
generator = {
    next(){...}, //具备next函数
    [Symbol.iterator](){...}, //具备(Symbol.iterator)属性
}

生成器建立函数(generator function)

开发者没法直接调用Generator建立生成器,只能使用生成器建立函数建立一个生成器。

生成器函数定义:只要function关键字和函数名之间加入一个*,该函数就是生成器函数,会返回一个生成器。

function *createGenerator(){}

const generator = createGenerator(); //获得一个生成器

console.log("next" in generator); //true
console.log(Symbol.iterator in generator); //true
console.log(generator.next === generator[Symbol.iterator]().next); //true

yield关键字

写在生成器内部,至关于暂停,配合next方法能够在生成器外部控制生成器函数内部代码的执行。

  1. yield关键字只能在生成器内部使用;
  2. 每次调用生成器的next方法后,代码会从上一个yield执行到下一个yield
  3. 若是没有生成器函数内部没有yield关键字,则里面的代码一行都不会执行;
  4. return返回的值做为next方法的done第一次为true时的value值;
  5. yield关键字后面的值会当作执行next方法时返回的value值;
  6. yield关键字表达式返回的值等于执行next方法时传入的参数。

举个例子:你们最好使用浏览器,手动调用generator.next(),一步一步看比较容易理解。

function* createGenerator() {
    let res;
    console.log("开始", res);
    
    res = yield 1; //将第2个next传入的参数做为返回值赋值给res

    console.log("打印1", res);
    
    res = yield 2; //将第3个next传入的参数做为返回值赋值给res
    
    console.log("打印2", res);
    
    return "结束"
}

const generator = createGenerator();
let result = generator.next();
console.log("调用第1次next的返回值:", result);

result = generator.next(result.value);
console.log("调用第2次next的返回值:", result);

result = generator.next(result.value);
console.log("调用第3次next的返回值:", result);

result = generator.next(result.value);
console.log("调用第4次next的返回值:", result);

//输出:
开始 undefined
调用第1次next: { value: 1, done: false }
打印1 1
调用第2次next: { value: 2, done: false }
打印2 2
调用第3次next: { value: '结束', done: true }
调用第4次next: { value: undefined, done: true }
利用生成器函数将异步代码转化为同步代码:
/**
 * 模拟一个请求
 */
function getData() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('数据');
        }, 2000)
    })
}

/**
 * 生成器函数,在这里写业务逻辑,能够将异步代码的写法转化为同步代码写法
 */
function* task() {
    console.log('获取数据中...');
    let result = yield getData(); //将异步代码转化为同步的写法
    console.log('获得数据:', result);
    //对数据进行后续处理...
}

/**
 * 运行生成器的通用函数
 */
function run(generatorFunc) {
    const generator = generatorFunc();
    next();

    function next(nextValue) {
        let result = generator.next(nextValue)
        if (result.done) { //迭代结束
            return;
        } else {
            const value = result.value;
            if (value instanceof Promise) {
                value.then(data => next(data));
            } else {
                next(value);
            }
        }
    }
}

run(task); //执行生成器函数代码

//输出:
获取数据中...
获得数据: 数据
相关文章
相关标签/搜索