Iterator:遍历器
为某种数据结构提供的一种统一的遍历机制
Iterator的遍历过程如下:
- 创建一个指针对象,指向当前数据结构的起始位置。遍历器对象本质上是一个指针对象
- 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员
- 第n次调用指针对象的next方法,指针就指向数据结构的第二个成员
- 不断调用next方法,直至指向数据结构的结束位置
每次调用next方法,返回一个包含数据结构当前成员信息的对象,指针移动
数据结构当前成员信息的对象有两个属性:value和done,value是当前成员的值,done是布尔型值,用于标识遍历是否结束
模拟next方法例子:
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 };
}
}
}
for ... of循环会自动去找被遍历数据结构的Iterator接口
for...of内部调用的是数据结构的Symbol.iterator方法
部分数据结构原生具有Iterator接口,如Array, Map, Set, String, TypedArray, 函数的arguements对象, NodeList对象,而有的数据结构没有原生的Iterator接口,如普通Object
但无论哪种数据结构,ES6规定数据结构默认的Iterator接口部署在数据结构的Symbol.iterator属性,调用Symbol.iterator方法会得到当前数据结构默认的遍历器生成函数
如果想要修改数据结构的默认Iterator接口:
const obj = { // obj是一个普通的Object
...
}
obj[Symbol.iterator] = function() {
return {
next: function() {
return {
value: 1,
done: true
};
}
}
}
使用Iterator接口的场景:
- 解构赋值
- 扩展运算符 (任何部署了Iterator接口的数据结构都可以转换为数组)
- yield*
yield* 后面可以跟一个可遍历结构
let generator = function* () {
yield 1;
yield* [2, 3, 4];
yield 5;
};
var iterator = generator();
iterator.next();
iterator.next();
...
for...of,Array.from(),Map(),Set(),WeakMap(),WeakSet(),Promise.all(),Promise.race()
字符串的Iterator接口
var s = 'hi';
var iterator = s[Symbol.iterator](); // 遍历器对象
iterator.next();
iterator.next();
iterator.next();
Iterator接口与Generator函数
Generator函数是Symbol.iterator方法的最简单实现
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
// 或下面的简洁写法:
let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
};
for (let x of obj) {
console.log(x);
}
遍历器对象的return()和throw()
next()方法是必须的
next()和throw()方法是可选的
在for...of中,如果循环提前退出(出错或break、continue语句)就会调用return()方法。常用于对象遍历完成前需要清理释放资源
throw()方法主要配合Generator函数使用,一般遍历器对象用不到
for...of对于普通的对象
不能直接使用
普通Object使用for...in会可以遍历key
如何遍历一个普通对象:
// 方法1
for (var key of Object.keys(obj)) {
console.log(key, obj[key])
}
// 方法2: 使用Generator函数将对象重新包装
function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
for (let [key, value] of entries(obj)) {
console.log(key, value)
}
Generator(生成器)
Generator函数会返回一个遍历器对象(指针对象),由此可以理解Generator函数是一个遍历器对象生成函数,返回的遍历器对象可以依次遍历Generator函数内部的每一个状态
Generator函数有两个特征:
- function命令和函数名之间有一个*
- 函数体内部使用yield语句定义不同的内部状态
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
};
var hw = helloWorldGenerator();
hw.next();
hw.next();
hw.next();
*yield语句逻辑在此不再赘述
Generator函数可以不用yield语句,就变成了一个暂缓执行的函数
function* f() {
console.log('执行');
}
var gen = f(); // 如果f是一个普通函数,则在这一步就执行了
setTimeout(function () {
gen.next()
}, 2000)
任意一个对象的Symbol.iterator方法等于该对象的遍历器对象生成函数
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
next方法的参数
function* f () {
for(var i = 0; true; i++) { // 无限运行的Generator函数
var reset = yield i;
if (reset) { i = -1 };
}
}
var g = f();
g.next();
g.next();
...
g.next(true);
yield语句的返回值是undefined,reset的值也是undefined,next方法带有一个参数时,这个参数被当做已经执行完的上一条yield语句返回值(yield的返回值不是undefined了)
for...of可以自动遍历Generator函数生成的Iterator对象
function* foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6
};
for (let v of foo()) {
console.log(v);
};
使用Generator函数和for...of循环实现斐波那契数列:
function* fibonacci () {
let [prev, curr] = [0, 1];
for (;;) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
for (let n of fibonacci()) {
if (n > 1000) break;
console.log(n);
}