迭代协议
迭代协议分为两个协议:可迭代协议和迭代器协议。
可迭代协议
可迭代协议允许 JavaScript 对象定义或定制它们的迭代行为。如果一个对象(或它原型链上的对象)实现了**[Symbol.iterator]方法,称该对象满足可迭代协议,也称该对象为可迭代对象**。
[Symbol.iterator]方法返回一个无参函数,该函数返回值为一个满足迭代器协议的对象,即迭代器对象。
一个可迭代对象是如何被迭代的?
当一个对象需要被迭代的时候(比如被置入一个 for...of
循环时),首先,会不带参数调用它的 [Symbol.iterator]
方法,然后使用此方法返回的迭代器获得要迭代的值。
迭代器协议
迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式。一个对象必须实现了具有如下特征的next()方法,才能称其为迭代器对象:
1、next() 方法是一个无参函数,返回值为一个特殊对象。
2、返回的特殊对象拥有两个属性:done(布尔值),value。
3、done的取值:如果迭代器可以产生序列中的下一个值,则为 false
;如果迭代器已将序列迭代完毕,则为 true
,这种情况下,value
是可选的,如果它依然存在,即为迭代结束之后的默认返回值(undefined)。
4、value的取值:任何 JavaScript 值。
5、next()
方法必须返回一个对象,该对象应当有两个属性: done
和 value
,如果返回了一个非对象值,则会抛出异常。
创造一个同时满足迭代器协议和可迭代协议的对象:
var myIterator = {
next: function() {
// ...
},
[Symbol.iterator]: function() { return this }
}
可迭代对象
内置可迭代对象有:String
、Array
、TypedArray
、Map
和 Set
,它们的原型对象都实现了 [Symbol.iterator]
方法。
自定义可迭代对象:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {// 生成器函数
yield 1;
yield 2;
yield 3;
};
[...myIterable]; // [1, 2, 3]
接收可迭代对象的内置API:
-
new Map([iterable])
-
new WeakMap([iterable])
-
new Set([iterable])
-
new WeakSet([iterable])
-
Promise.all(iterable)
-
Promise.race(iterable)
-
Array.from(iterable)
…
例如:
new Map([[1, 'a'], [2, 'b'], [3, 'c']]).get(2); // "b"
let myObj = {};
new WeakMap([
[{}, 'a'],
[myObj, 'b'],
[{}, 'c']
]).get(myObj); // "b"
new Set([1, 2, 3]).has(3); // true
new Set('123').has('2'); // true
new WeakSet(function* () {
yield {};
yield myObj;
yield {};
}()).has(myObj); // true
需要可迭代对象的语法:
一些语句和表达式需要可迭代对象,比如 for...of
循环、展开语法、yield*
,和解构赋值:
for(let value of ["a", "b", "c"]){
console.log(value);
}
// "a"
// "b"
// "c"
[..."abc"]; // ["a", "b", "c"]
function* gen() {
yield* ["a", "b", "c"];
}
gen().next(); // { value: "a", done: false }
[a, b, c] = new Set(["a", "b", "c"]);
a // "a"
格式不佳的可迭代对象:如果一个可迭代对象的 [Symbol.iterator]
方法不能返回迭代器对象,那么可以认为它是一个格式不佳的(Non-well-formed)可迭代对象 。
使用这样的可迭代对象很可能会导致如下的运行时(runtime)异常,或者不可预料的表现:
var nonWellFormedIterable = {}
nonWellFormedIterable[Symbol.iterator] = () => 1
[...nonWellFormedIterable] // TypeError: [] is not a function
迭代器示例
返回简单迭代器的函数:
function makeIterator(array) {
let nextIndex = 0;
return {
next: function () {
if(nextIndex < array.length) {
return {
value: array[nextIndex++],
done: false
}
}
else {
return {
done: true
}
}
}
}
}
let it = makeIterator(['哟', '呀']);
console.log(it.next().value); // '哟'
console.log(it.next().value); // '呀'
console.log(it.next().done); // true
返回无穷迭代器的函数:
function idMaker() {
let index = 0;
return {
next: function() {
return {
value: index++,
done: false
};
}
};
}
let it = idMaker();
console.log(it.next().value); // '0'
console.log(it.next().value); // '1'
console.log(it.next().value); // '2'
// ...
使用生成器:
// 简单生成器
function* makeSimpleGenerator(array) {
let nextIndex = 0;
while(nextIndex < array.length) {
yield array[nextIndex++];
}
}
let gen = makeSimpleGenerator(['哟', '呀']);
console.log(gen.next().value); // '哟'
console.log(gen.next().value); // '呀'
console.log(gen.next().done); // true
// 无穷生成器
function* idMaker() {
let index = 0;
while (true) {
yield index++;
}
}
let gen = idMaker();
console.log(gen.next().value); // '0'
console.log(gen.next().value); // '1'
console.log(gen.next().value); // '2'
// ...
es6类中的迭代器(自定义可迭代对象):
class SimpleClass {
constructor(data) {
this.data = data
}
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return {value: this.data[index++], done: false}
} else {
return {done: true}
}
}
}
}
}
const simple = new SimpleClass([1,2,3,4,5]);
for (const val of simple) {
console.log(val) //1 2 3 4 5
}
生成器对象到底是一个迭代器对象,还是一个可迭代对象?
结论是:生成器对象既是迭代器,也是可迭代对象。
let aGeneratorObject = function* (){
yield 1;
yield 2;
yield 3;
}();
typeof aGeneratorObject.next;
// 返回"function", 因为有一个next方法,所以这是一个迭代器
typeof aGeneratorObject[Symbol.iterator];
// 返回"function", 因为有一个@@iterator方法,所以这是一个可迭代对象
aGeneratorObject[Symbol.iterator]() === aGeneratorObject;
// 返回true, 因为@@iterator方法返回自身(即迭代器),所以这是一个格式良好的可迭代对象
[...aGeneratorObject];
// 返回[1, 2, 3]
console.log(Symbol.iterator in aGeneratorObject)
// 返回true, 因为@@iterator方法是aGeneratorObject的一个属性