Iterator和Generator
Iterator 和 Generator
Iterator(迭代器)
详细文档:ECMAScript 6 入门-Iterator 和 for...of 循环
Iterator(迭代器)的概念
Iterator 即遍历器对象
Generator 函数调用后可以返回一个遍历器对象 Iterator
所有可以使用 for of 遍历的对象内部都实现时了 Iterator 接口 比如:
通过Arrary.prototype[Symbol.iterator]()
可以返回 Iterator
{// Arrary 内部的遍历器对象
__proto__: Array Iterator{
next: ƒ next() // 调用 next 方法将 指针指向下一个元素
Symbol(Symbol.toStringTag): "Array Iterator"
__proto__: Object
}
}
当然也可直接实现一个遍历器对象
var it = makeIterator(["a", "b"]);
it.next(); // { value: "a", done: false }
it.next(); // { value: "b", done: false }
it.next(); // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function () {
return nextIndex < array.length
? { value: array[nextIndex++], done: false }
: { value: undefined, done: true };
},
};
}
调用 next 方法, 返回{ value: xxx, done: boolean }
,通过判断 done 是否为 true,来判断遍历是否结束
调用 Iterator 接口的场合
有一些场合会默认调用 Iterator 接口(即Symbol.iterator
方法),除了下文会介绍的for...of
循环,还有几个别的场合。
(1)解构赋值
对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator
方法。
let set = new Set().add("a").add("b").add("c");
let [x, y] = set;
// x='a'; y='b'
let [first, ...rest] = set;
// first='a'; rest=['b','c'];
(2)扩展运算符
扩展运算符(...)也会调用默认的 Iterator 接口。
// 例一
var str = "hello";
[...str]; // ['h','e','l','l','o']
// 例二
let arr = ["b", "c"];
["a", ...arr, "d"];
// ['a', 'b', 'c', 'd']
上面代码的扩展运算符内部就调用 Iterator 接口。
实际上,这提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结构,转为数组。也就是说,只要某个数据结构部署了 Iterator 接口,就可以对它使用扩展运算符,将其转为数组。
let arr = [...iterable];
(3)yield*
yield*
后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
let generator = function* () {
yield 1;
yield* [2, 3, 4];
yield 5;
};
var iterator = generator();
iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
iterator.next(); // { value: 3, done: false }
iterator.next(); // { value: 4, done: false }
iterator.next(); // { value: 5, done: false }
iterator.next(); // { value: undefined, done: true }
(4)其他场合
由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。
- for...of
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()(比如
new Map([['a',1],['b',2]])
) - Promise.all()
- Promise.race()
Generator(生成器)
详细文档:ECMAScript 6 入门-Generator 函数的语法
建议看:JavaScript 高级程序设计(第 4 版)中关于生成器的描述
基本概念
Generator 函数 是生成器函数,Generator 函数 返回 Generator(生成器)
Generator 是一个状态机,封装了多个内部状态(本人的理解:是一个可以控制内部执行步骤的 函数)
Generator 生成器 内部实现了 Iterator (迭代器)接口,因此具有 next() 方法。调用这个方法会让生
成器开始或恢复执行
控制内部执行步骤
调用 generatorFn 生成器函数的时候并不会执行内部代码
generator 调用 next 方法, 开始执行 generatorFn 内部代码
遇到 yield 关键字将停止执行代码,并将 yield 后面的值返回
再次调用 next 方法 将从上一个 yield 所在行开始执行
如果 next 中有参数将传递给开始执行所在行的 yield,如果是首次调用 next 将没有 yield 可以供其赋值
遇到 return 关键字将返回 { value: return 后面的值, done: true }
如果函数内没有 return 也没有 yield 的了 将返回 { value: undefined, done: true }
正常执行顺序
function* generatorFn() {
console.log("0->yield1");
yield "yield1"; // 停止 返回 yield1
console.log("yield1->yield2");
yield "yield2"; // 停止 返回 yield2
console.log("yield2->end");
}
const generator = generatorFn();
console.log("next1", generator.next());
// 0->yield1
// next1 { value: 'yield1', done: false }
console.log("next2", generator.next());
// yield1->yield2
// next2 { value: 'yield2', done: false }
console.log("next3", generator.next());
// yield2->end
// next3 { value: undefined, done: true }
next 传参
function* generatorFn(params) {
console.log(params);
console.log("yield1", yield "yield1"); // 遇到 yield ,yield所在行的代码都不会执行,但是 yield 后边的参数会返回
console.log("yield2", yield "yield2"); // 不会执行console.log,下次调用next时才会执行console.log以及后面的代码
console.log("end");
}
const generator = generatorFn("foo");
// ***第一次的bar并不会赋值给yield1关键字 因为这一次调用是为了开始执行生成器函数,第二次调用yield1的值变成baz***
console.log("next1", generator.next("bar"));
// foo
// next1 { value: 'yield1', done: false }
console.log("next2", generator.next("baz"));
// yield1 baz
// next2 { value: 'yield2', done: false }
console.log("next3", generator.next("qux"));
// yield2 qux
// end
// next3 { value: undefined, done: true }
带 return 的
function* helloWorldGenerator() {
yield "hello";
yield "world";
return "ending";
}
var hw = helloWorldGenerator();
hw.next();
// { value: 'hello', done: false }
hw.next();
// { value: 'world', done: false }
hw.next();
// { value: 'ending', done: true }
hw.next();
// { value: undefined, done: true }