ES6+ 迭代协议
1. 前言
上一节我们对 ES6 新增的 for...of
做了深入的讲解,它可以用于字符串、数组、类数组、以及新增的数据结构 Map/Set 等进行遍历。但是这些能够使用 for...of
进行遍历的都有一个共同的特性 —— 可迭代。那什么是可迭代呢?
ECMAScript 2015 在一组补充规范中规定了两个协议:可迭代协议和迭代器协议。这两个规定不是新的内置实现或语法,而是协议。这些协议可以被任何数据结构遵循,从而可以实现自定义的遍历,而这些遵守迭代协议的数据结构是可以被 for...of
遍历的。有了这样的一个规范,我们所定义的数据结构就会更加丰富了,本节我们将深入 ES6 中的迭代。
2. 可迭代协议和迭代器协议
2.1 可迭代协议
什么是可迭代协议?可以通过 JavaScript 对象定义或定制迭代行为,在 JavaScript 中有一些内置类型并且满足内置的可迭代对象,具有迭代的行为。如 Array、Map、Set 等。还有一个比如字面量对象(Object)则没有,如果要对 Object 进行迭代的话需要使用 for...in
循环,但是 for...in
在循环时需要判断是否是自身属性。所以很多时候如果想用 for...of
进行迭代时就需要使用对象上的 Object.keys()
等方法提取对象中的 keys 后再去进行遍历操作。
在 ES6 中可迭代协议规定想要成为可迭代对象,这个对象必须实现 @@iterator
方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator
的属性,可通过常量 Symbol.iterator
访问该属性。所以,一个对象满足可迭代协议的关键在于实现 Symbol.iterator
方法,这个方法的返回值是一个符合迭代器协议的对象,并且是一个无参数的函数。
2.2 迭代器协议
迭代器协议定义了产生一系列值的一个标准方式,迭起协议规定需要返回一个带 next()
方法的对象。 next()
可以被多次执行,每次执行都会返回一个对象,该对象包含两个属性,done
和 value
:
-
done
是一个 boolean,在没有迭代完时返回 false,迭代完成后返回 true; -
value
就是被迭代的返回值,当done
为 true 时可以省略。
3. 迭代器
这里说的迭代器是遵循上面两个协议来实现的,在满足两个协议时,我们可以显式地通过不断调用 next () 方法去进行迭代。在迭代一个迭代器后,我们称之为消耗了这个迭代器而,且每个迭代器只能执行一次。下面我们来看看怎么实现一个迭代器:
var obj = {}
obj[Symbol.iterator] = function() {
let index = ;
return {
next() {
if (index <= ) {
return {value: index++, done: false}
} else {
return {done: true}
}
}
}
}
上面的代码中根据可迭代协议给 obj 对象添加一个 Symbol.iterator
方法,再根据迭代器协议返回一个 next()
方法,在每次消耗 next()
时对 index 进行加 1 操作。当 index 大于 10 的时候结束迭代行为,之后再消耗 next()
返回值不变。
var iterator = obj[Symbol.iterator]();
var s = iterator.next();
while(!s.done) {
console.log(s.value);
s = iterator.next();
}
// 1
// 2
// ...
执行上面的代码,在浏览器的控制台中,可以看到大于的结果是 1 到 10。上面是我们手动执行消耗 next()
的方式,上面我们也说了,只要满足迭代协议就可以被 for...of
循环,那是不是真的是这样的呢?下面我们就使用 for...of
对 obj 进行循环。
for (let i of obj) {
console.log(i)
}
// 1
// 2
// ...
4. 小结
本节我们主要学习了两个协议 —— 可迭代协议和迭代器协议,并且通过这两个协议实现了一个迭代器。通过这个迭代器我们知道,在满足这两个协议后就可以使用 for...of
进行循环,并且我们进行显示调用进行了验证。
插入案例