目录
迭代器与生成器(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 的停顿来化异步为同步,而不是使用 回调函数。