一、简介
1.1 集合对象迭代器
迭代器是一种常用的设计模式,人们常用它来遍历集合对象。在ES6中,有Array、Map和Set三种常用的集合对象,他们都内建了三种迭代器:
- entries() 返回键值对迭代器
- keys() 返回键名迭代器
- values() 返回集合值迭代器
所有的迭代器对象都有一个next()方法,每次调用都会返回一个{value: value, done: Boolen}对象,当没有可返回数据时done的值为true。for-of循环就是每次执行的时候都会去调用迭代器的next(),直至done属性的值为true。相比于for循环,for-of循环可以避开集合的索引跟踪只需关注集合中需要处理的内容。
for(let entry of map.entries()){}
for(let key of array.keys()){}
for(let value of set.values()){}
1.2 默认的迭代器
Array、Map和Set虽然都有三个内建迭代器,但是他们默认的迭代器是不同的,比如Map的默认迭代器是entries,Set的默认迭代器是values。我们可以通过Symbol.iterator来获取可迭代对象的默认迭代器。例如:
let map = new Map();
let iterator = map[Symbol.iterator]();
此外,我们也可通过Symbol.iterator创建一个可迭代对象:
let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of items) {
yield item;
}
}
};
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
console.log(x);
}
二、原理浅析
先看一下高级迭代器的代码。
function *createIterator() {
console.log('begin');
let a = yield 1;
console.log('a', a);
let b = yield a + 2;
console.log('b', b);
yield b + 3;
console.log('finish');
}
let iterator = createIterator();
iterator.next(0); // return {value: 1, done: false}
// begin
iterator.next(2); // return {value: 4, done: false}
// a 2
iterator.next(4);// return {value: 6, done: false}
// b 4
iterator.next(6);// return {value: undefined, done: true}
// finish
通过上面高级迭代器的代码和输出,我们可以看出迭代器的运行原理。生成器Generator的代码根据yield
关键字被拆分成了多个函数,类似于split('yield')
的效果。在运行了iterator.next(0);
之后createIterator执行了console.log('begin'); yield 1;
。至于let a = yield 1;
,相当于return 1
和let a = next()
,所以a的值是iterator.next(2);
的参数2。
三、async/await
ES6发布了Generator和Promise两种新特性,不久之后async/await语法被提上议程并在ES7中如约而至。其初衷是用async标记的函数用来代替生成器,用await代替yield来调用函数。然而,async/await却是对Promise和Generator的封装,async/await用Babel
转换成ES6的之后是Promise和Generator。不仅如此,浏览器也会像Babel
一样去转换async/await。
/**
* Bable转换func函数
*
* async function func() {
* await promise();
* console.log('finish...');
* }
*/
function _asyncToGenerator(fn) {
return function () {
var gen = fn.apply(this, arguments);
return new Promise(function (resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(function (value) {
step("next", value);
}, function (err) {
step("throw", err);
});
}
}
return step("next");
});
};
}
let promise = new Promise(resolve => resolve(1));
let func = (() => {
let _ref = _asyncToGenerator(function*() {
yield promise;
console.log('finish...');
});
return function func() {
return _ref.apply(this, arguments);
};
})();