迭代协议(Iteration Protocols)是 ESMAScript 2015 (ES6) 的补充内容,它还只是一种 约定,还没有成为内置语法。
任何对象都可以按照约定实现迭代协议。
迭代协议包含两个内容:可迭代协议(Iterable)和 迭代器协议(Iterator)。
可迭代协议
要想使一个普通对象成为 可迭代 的对象,只需要给它添加一个 [Symbol.iterator]
属性。
该属性值是一个函数,无传入参数且返回一个 迭代器(Iterator)。
只有可迭代对象才能用 for ... of
迭代取值。
迭代器协议
要想使一个对象成为 迭代器(Iterator),只需要给它实现 next()
方法。
该方法接受零个或者一个参数,返回一个至少包含 done
和 value
两个属性的对象:
done
(boolean) 标志序列是否迭代完了,迭代完成之后应该返回true
;value
迭代器的返回值,在done
为true
时可以忽略,此时为undefined
。
二者的区别和关系
对象实现了 可迭代协议,说明它可以被迭代,例如 for ... of
,但是并没有说明每次迭代应该怎么返回值,以及返回什么值。
可迭代协议 中 [Symbol.iterator]
属性返回的是一个 迭代器,迭代器负责在每次迭代时生成具体的值。
实现了 next()
方法的对象叫做 迭代器,通过调用其 next()
方法可以迭代获取序列的值。
for ... of
等消费可迭代对象的语法就是通过调 可迭代对象 关联的 迭代器 的 next()
方法实现遍历的。
迭代协议的例子
数组的原型对象实现了 可迭代协议:
Array.prototype[Symbol.iterator]
// ƒ values() { [native code] }
Array.prototype[Symbol.iterator]()
// Array Iterator {}
手动使用数组对象的迭代器对序列进行迭代:
const x = [1, 2, 3]
const iterator = x[Symbol.iterator]()
iterator.next()
// {value: 1, done: false}
iterator.next()
// {value: 2, done: false}
iterator.next()
// {value: 3, done: false}
iterator.next()
// {value: undefined, done: true}
可以看出,数组对象 x
是一个 可迭代对象,x[Symbol.iterator]
是一个 迭代器。
此外,String
, TypedArray
, Set
和 Map
也都默认实现了可迭代协议和迭代器协议。
一个对象可以同时实现可迭代协议和迭代器协议,即它既是可迭代对象,又是迭代器:
const myIterableIterator = {
next: function() { /*...*/ },
[Symbol.iterator]: function() { return this }
}
但是一般不建议这么干。
扩展语法(...
)也会自动调用待扩展对象的迭代器取出序列:
console.log([...x])
迭代器的例子
只有满足 迭代器协议 的约定,可以用多种方法创建迭代器。
例如通过保存索引位置,创建指定序列的迭代器:
function makeIterator(array) {
let index = 0;
return {
next: function() {
return index < arr.length ? {
value: arr[index++],
done: false
} : {
done: true
}
}
}
}
const it = makeIterator([1, 2, 3])
console.log(it.next()) // {value: 1, done: false}
console.log(it.next()) // {value: 2, done: false}
console.log(it.next()) // {value: 3, done: false}
console.log(it.next()) // {value: undefined, done: true}
例如创建可以无限迭代的迭代器:
function makeInfiniteIterator() {
let index = 0
return {
next: function() {
return { done: false, value: index++ }
}
}
}
例如通过生成器函数创建迭代器:
function* makeSimpleGenerator(array) {
let index = 0;
while (index < array.length) {
yield array[index++];
}
}
function* makeInfiniteGenerator() {
let index = 0;
yield index++;
}
通过类实现迭代器(ES6):
class SimpleIterator {
constructor(arr) {
this.arr = arr;
}
[Symbol.iterator]() {
let index = 0;
return {
// 因为需要用到 this,所以用箭头函数
next: () => {
return index < this.arr.length ? { done: false, value: this.arr[index++] } : { done: true };
}
}
}
}
生成器与迭代器/可迭代对象的关系
生成器 既是可迭代对象,又是迭代器。
const g = function* () {
yield 1;
yield 2;
}();
console.log(g === g[Symbol.iterator]()) // true
Reference
👉 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
评论区