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
*/