ES6的迭代器(Iterator)和生成器(Generator)

Iterator(迭代器)

遍历器(Iterator)是一种机制(接口),为各种不同的数据提供统一的访问机制,任何数据结构只要部署Iterator接口,就可以完成遍历操作【for-of循环】,依次处理该数据结构的所有成员。

Iterator规范的机制:

  • 拥有next方法用于依次遍历数据结构的成员
  • 每一次遍历返回的结果都是一个对象{done:true/false,value:xxx}
    • done:记录是否遍历完成,布尔值:true(完成) / false(未完成)
    • value:当前遍历的结果

手动实现Itrator 类:

class Itrator {
    constructor(assemble) {
        let self = this;
        self.assemble = assemble;
        self.index = 0; //计数器

    };
    next() {
        let self = this,
            assemble = self.assemble;
        if (self.index >= assemble.length - 1) {
            return {
                value: assemble[self.index++],
                done: true
            }
        }
        return {
            value: assemble[self.index++],
            done: false
        }
    }
}
let itator = new Itrator([1, 2, 3, 4, 5, 6]);
console.log(itator.next()); //{value: 1, done: false}
console.log(itator.next()); //{value: 2, done: false}
console.log(itator.next()); //{value: 3, done: false}
console.log(itator.next()); //{value: 4, done: false}
console.log(itator.next()); //{value: 5, done: false}
console.log(itator.next()); //{value: 3, done: true}

拥有Symbol.iterator属性的数据结构(值),被称为可遍历的,可以基于for-of循环处理:

  • 数组
  • 部分类数组:arguments 、NodeList、HTMLCollection…
  • String
  • Set
  • Map
  • generator Object

数组的for-of原理:

//Array的原型有一个属性Symbol[Symbol.iterator]: function(){...}
let arr = [1, 2, 3, 4, 5, 6];
// arr[Symbol.iterator] = function() {…} 每个数组都拥有一个属性【数组原型上的内置属性】
// arr[Symbol.iterator]()->执行后, 返回一个具备iterator规范的对象【拥有next方法的对象】itor
for (ler item of arr){
	//每一轮循环都执行一次next
	//itor.next() item->value;done如果是false,则进入下一轮循环
	//……
	//done称为true,则整个循环结束
	console.log(item);
};
//================
//重构数组的Symbol.iterator方法一:
arr[Symbol.iterator] = function() {
    let assemble = this,
        index = 0;
    return {
        next: function() {
            if (index > assemble.length) {
                return {
                    value: undefined,
                    done: true
                }
            };
            return {
                value: assemble[index++],
                done: false
            }
        }
    }
};
//重构数组的Symbol.iterator方法二:【利用自己写的Iterator类】
arr[Symbol.iterator] = function() {
	return new Iterator(this);
};

实现对象的Symbol.iterator属性:

对象默认不具备Symbol.iterator属性,属于不可遍历的数据结构:

let obj = {
    name: "kk",
    age: 10,
    sex: "女"
}
for (let item of obj) {
    console.log(item); //Uncaught TypeError: obj is not iterable
}

手动实现对象数据类型的for-of循环:

let obj = {
    name: "kk",
    age: 10,
    sex: "女"
}
Object.prototype[Symbol.iterator] = function() {
    let assemble = this,
        keys = Object.keys(assemble),
        index = 0;
    return {
        next: function() {
            if (index > keys.length - 1) {
                return {
                    value: undefined,
                    done: true
                }
            };
            return {
                value: assemble[keys[index++]],
                done: false
            }
        }
    }
};
for (let item of obj) {
    console.log(item);
}

手动实现类数组的for-of循环

//类数组也没有[Symbol.iterator]属性,不具备for-of循环的iterator机制
let obj = {
    0: 10,
    1: 20,
    2: 30,
    length: 3
}
obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
for (let item of obj) {
    console.log(item);
}

JQ实例也是类数组集合,所以JQ源码内部也为JQ实例实现了iterator机制,是的JQ实例可以基于for-of实现循环:

if ( typeof Symbol === "function" ) {
	jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
}

Generator(生成器)

生成器(Generator)对象是由一个 generator function 返回的,并且它符合可迭代协议和迭代器协议【Iterator协议】。

function* fn(x) {
    console.log(x);
    return x * 10;
};
// 默认返回一个具备迭代器规范的generator实例对象 =>生成器函数执行返回一个迭代器。
// 虽然fn后面加小括号了,但是函数体内部代码并没有执行。
let gen = fn(100);
console.log(gen.next());// 100 && generator.js:8 {value: 1000, done: true}

//----------
function* generator() {
    console.log("A");
    yield 10;
    console.log("B");
    yield 20;
    console.log("C");
    yield 30;
    console.log("D");
    yield 50;

};
let iterator = generator();
console.log(iterator.next()); //A && {value: 10, done: false}
console.log(iterator.next()); //B && {value: 20, done: false}
console.log(iterator.next()); //C && {value: 30, done: false}
console.log(iterator.next()); //D && {value: 10, done: false}
console.log(iterator.next()); //{value: undefined, done: true}

//===========
//每一次执行next方法都会去函数体中执行代码
//  从开始或者上一次yield结束的位置继续向下执行,直到遇到新的yield结束
//	每次返回的对象 value->yield后面的值/return后面的值 done -> false/true(遇到return是true)
function* generator(x) {
    console.log("A");
    yield x;
    console.log("B");
    yield 2 * x;
    console.log("C");
    yield 3 * x;
    console.log("D");
    return 5 * x;
};
let iterator = generator(10);
console.log(iterator.next()); //A && {value: 10, done: false}
console.log(iterator.next()); //B && {value: 20, done: false}
console.log(iterator.next()); //C && {value: 30, done: false}
console.log(iterator.next()); //D && {value: 10, done: true}  done:遇到return是true
console.log(iterator.next()); //{value: undefined, done: true}
  • function后面加一个星号(*) 就是生成器函数(generator)
  • generator函数执行,默认返回一个具备迭代器规范的实例对象
    • value:当前迭代的值
    • done:布尔值,用来表示当前迭代器是否迭代完成
  • 可以配合yield关键字使用,yield用来说明next函数调用时返回的值
  • generator函数不能被new执行【返回是一个generator函数的实例对象】
  • generator函数实例的原型链:
    • 一个具备迭代器规范的对象[generator实例对象gen] ->
    • gen.__ptoto__ ->
    • fn.prototype ->
    • GeneratorFunction.prototype[next / return / throw / Symbol.toStringTag] ->
    • xxx.prototype [Symbol.iterator] ->
    • Object.prototype
  • 使用生成器函数的目的:想一步一步管控结果
  • 可以解决回调地狱问题

Generator实例的方法

[iter].next()

  • 第一次传递的值没用
  • 每一次执行next传递的值,是作为上一次yield的的返回值处理的

[iter].return([val]):返回给定的值并结束生成器。

  • [val] : 需要返回的值
  • 返回该函数参数中给定的值。-> g.return("foo"); // { value: "foo", done: true }

[iter].throw():

  • 用于抛出的异常。
  • 该异常通常可以通过 try…catch 块进行捕获

Generator函数的嵌套

yield* 后面跟着一个新的itor,后期执行到这的时候,会进入到新的generator中执行。

function* generator1() {
    yield 10;
    yield 20;
};

function* generator2() {
    yield 30;
    yield generator1();
    yield 40;
};
let itor = generator2();
console.log(itor.next()); //{value: 30, done: false}
console.log(itor.next()); //{value: generator1, done: false}
console.log(itor.next()); //{value: 40, done: false}
console.log(itor.next()); //{value: undefined, done: true}
//=======

function* generator1() {
    yield 10;
    yield 20;
};

function* generator2() {
    yield 30;
    yield* generator1();//后期函数会进入到新的generator中执行
    yield 40;
};
let itor = generator2();
console.log(itor.next()); //{value: 30, done: false}
console.log(itor.next()); //{value: 10, done: false}
console.log(itor.next()); //{value: 20, done: false}
console.log(itor.next()); //{value: 40, done: false}

模拟实现数据请求

//模拟数据请求:执行方法,发送一个数据请求,传递的是请求的时间,请求成功后的结果也是这个值
const query = function(interval) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve(interval);
        }, interval);
    });
};
//方法一:promise方法
query(1000).then(function(result) {
    console.log("第一个请求成功,结果是:", result);
    return query(2000);
}).then(function(result) {
    console.log("第二个请求成功,结果是:", result);
    return query(3000);
}).then(function(result) {
    console.log("第三个请求成功,结果是:", result);
});
//  1s:第一个请求成功,结果是: 1000
// +2s:第二个请求成功,结果是: 2000
// +3S:第三个请求成功,结果是: 3000

//方法二:generator实现
function* generator() {
    let result;
    result = yield query(1000);
    console.log("第一个请求成功,结果是:", result);

    result = yield query(2000);
    console.log("二个请求成功,结果是:", result);

    result = yield query(3000);
    console.log("第三个请求成功,结果是:", result);
};
let itor = generator("Open");
console.log(itor.next("A")); //{value: Promise, done: false}
console.log(itor.next("B")); //第一个请求成功,结果是:B &&{value: Promise, done: false}
console.log(itor.next("C")); //第二个请求成功,结果是:C &&{value: Promise, done: false}
console.log(itor.next("D")) //第三个请求成功,结果是:D &&{value: undefined, done: false}

通过一个函数执行全部操作:

function isPromise(x) {
    if (x == null) return false;
    if (/^(object | function)$/i.test(typeof x)) {
        if (typeof x.then === "function") return true;
    };
    return false;
};
//模拟数据请求:执行方法,发送一个数据请求,传递的是请求的时间,请求成功后的结果也是这个值
const query = function(interval) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve(interval);
        }, interval);
    });
};

function AsyncFunction(generator, ...params) {
    return new Promise(resolve => {
        let itor = generator(...params);
        const next = x => {
            let { value, done } = itor.next(x);
            if (done) {
                resolve(value);
                return;
            }
            if (!isPromise(x)) value = Promise.resolve(value);
            value.then(result => next(result))
                .catch(reason => itor.throw(reason));
        };
        next();
    })
};
AsyncFunction(function* generator(x, y) {
    console.log(x, y);
    let result;
    result = yield query(1000);
    console.log("第一个请求成功,结果是:", result);

    result = yield query(2000);
    console.log("第二个请求成功,结果是:", result);

    result = yield query(3000);
    console.log("第三个请求成功,结果是:", result);
}, 100, 200).then(() => {
    console.log("全部处理完成")
});
//基于co.js原理实现

结果展示:
在这里插入图片描述
ES6新增的async+await也可以实现:async+await是generator的“语法糖”

(async function() {
    let result;
    result = await query(1000);
    console.log("第一个请求成功,结果是:", result);

    result = await query(2000);
    console.log("第二个请求成功,结果是:", result);

    result = await query(3000);
    console.log("第三个请求成功,结果是:", result);
})();
/*
第一个请求成功,结果是: 1000
第二个请求成功,结果是: 2000
第三个请求成功,结果是: 3000
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值