JavaScript Guidebook

JavaScript 完全知识体系

Iterator

遍历器(Iterator)为各种不同的数据结构提供统一的接口访问机制。任何数据结构只要部署 Iterator 接口,即无须初始化集合,以及索引的变量,而是使用迭代器对象的 next 方法,依次返回集合的下一项值,便于逐项处理该数据结构的所有成员,偏向程序化。

ES5 中的 Loop 的缺点

循环方法缺点
for 语句条件部分冗杂;多层嵌套需要定义多个变量,复杂度过高
for...in 语句只能获取对象键名,需要通过属性访问器的方括号形式获取键值;只适用于对象,其他数组类型不适用
forEach 方法不能中断,跳出循环

迭代器

迭代器是带有特殊接口的对象。含有一个 next() 方法,调用后返回一个包含两个属性的对象,分别是 value(表示属性值) 和 done(表示迭代是否完成)。当迭代完成后,即 done 属性为 true 时,调用 next() 无效。

模拟 Iterator 的内部实现(实质是一个返回迭代器对象的工厂函数):

let iterable = [1, 2, 3]
function createIterator(array){
let count = 0
return {
next: function(){
return count < array.length ?
{value: array[count], done: false}:
{value: undefined, done: true}
}
}
}
let myIterator = createIterator(iterable)
myIterator.next() // {value: 1, done: false}
myIterator.next() // {value: 2, done: false}
myIterator.next() // {value: 3, done: false}
myIterator.next() // {value: undefined, done: true}

迭代器协议

迭代器对象不是新的语法或新的内置对象,而一种协议( 迭代器协议),所有遵守这个协议的对象,都可以称之为迭代器。也就是说我们上面 ES5 的写法得到的对象遵循迭代器协议,即包含 next,调用 next 返回一个result{value,done}

可迭代对象

满足可迭代协议的对象是可迭代对象。

可迭代协议:对象的 Symbol.iterator 属性的值是一个无参函数,该函数返回一个迭代器。

下面是数组的 Symbol.iterator 属性实现的迭代。

const arr = [0, 1, 2]
const iter = arr[Symbol.iterator]()
iter.next() // Output: {value: 0, done: false}
iter.next() // Output: {value: 1, done: false}
iter.next() // Output: {value: 2, done: false}
iter.next() // Output: {value: undefined, done: true}

内置可迭代对象

原生具备 Iterator 接口的数据结构:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

自定义可迭代对象

我们可以实现一个自己的可迭代对象:

let myIterable = {}
myIterable[Symbol.iterator] = function*(){
yield 1
yield 2
yield 3
}
[...myIterable] // Output: [1, 2, 3]

接受可迭代对象的内置 API

许多 API 接受可迭代对象, 例如:Map([iterable])WeakMap([iterable])Set([iterable])WeakSet([iterable])

var myObj = {};
new Map([[1,"a"],[2,"b"],[3,"c"]]).get(2); // "b"
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);

另外还有 Promise.all(iterable)Promise.race(iterable) 以及 Array.from()

for...of 循环

for...of 接受一个可迭代对象(Iterable),或者能被强制转换/包装成一个可迭代对象的值。遍历时,for...of 会获取可迭代对象的 [Symbol.iterator](),对该迭代器逐次调用 next(),直到迭代器返回对象的 done 属性为 true 时,遍历结束,不对该 value 处理。

for...of 循环实例:

var a = ["a","b","c","d","e"];
for (var val of a) {
console.log( val );
}
// "a" "b" "c" "d" "e"

转换成普通 for 循环示例,等价于上面 for...of 循环:

var a = ["a","b","c","d","e"];
for (var val, ret, it = a[Symbol.iterator]();
(ret = it.next()) && !ret.done;
) {
val = ret.value;
console.log( val );
}
// "a" "b" "c" "d" "e"

参考文章