内置的迭代器
迭代器是 ES6
的一个重要部分,正因为此,你无须为许多内置类型创建你自己的迭代器,语言已经默认包含它们了。只有当内置的迭代器无法满足你的需要时,才有必要创建自定义迭代器,这最常发生在定义你自己的对象或类时,否则完全可以依靠内置的迭代器来完成工作。最常用的迭代器或许就是集合上的迭代器。
集合的迭代器
ES6
具有三种集合对象类型:数组、Map
与 Set
。这三种类型都拥有如下的迭代器,有助于探索它们的内容:
-
entries():返回一个包含键值对的迭代器;
-
values():返回一个包含集合中的值的迭代器;
-
keys():返回一个包含集合中的键的迭代器。
你可以调用上述方法之一来提取集合中的迭代器。
entries() 迭代器
entries()
迭代器会在每次 next()
被调用时返回一个双项数组,此数组代表了集合中每个元素的键与值:对于数组来说,第一项是数值索引;对于 Set
,第一项也是值(因为它的值也会被视为键);对于 Map
,第一项就就是键。
这里有一些使用此迭代器的范例:
let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ECMAScript 6");
data.set("format", "ebook");
for (let entry of colors.entries()) {
console.log(entry);
}
for (let entry of tracking.entries()) {
console.log(entry);
}
for (let entry of data.entries()) {
console.log(entry);
}
调用 console.log()
输出了以下内容:
[0, "red"]
[1, "green"]
[2, "blue"]
[1234, 1234]
[5678, 5678]
[9012, 9012]
["title", "Understanding ECMAScript 6"]
["format", "ebook"]
此代码在每种集合类型上使用了 entries()
方法来提取迭代器,并且使用 for-of
循环来迭代它们的项。此处的控制台输出说明了每个对象的键与值是如何被成对返回的。
values() 迭代器
values()
迭代器仅仅能返回存储在集合内的值,例如:
let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ECMAScript 6");
data.set("format", "ebook");
for (let value of colors.values()) {
console.log(value);
}
for (let value of tracking.values()) {
console.log(value);
}
for (let value of data.values()) {
console.log(value);
}
此代码输出了如下内容:
"red"
"green"
"blue"
1234
5678
9012
"Understanding ECMAScript 6"
"ebook"
正如本例所显示的,调用 values()
迭代器返回了每种类型中包含的准确数据,而无须提供这些数据在集合内的任何位置信息。
keys() 迭代器
keys()
迭代器能返回集合中的每一个键。对于数组来说,它只返回了数值类型的键,永不返回数组的其他自有属性;Set
的键与值是相同的,因此它的 keys()
与 values()
返回了相同的迭代器;对于 Map
,keys()
迭代器返回了每个不重复的键。这里有个例子演示了这三种情况:
let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ECMAScript 6");
data.set("format", "ebook");
for (let key of colors.keys()) {
console.log(key);
}
for (let key of tracking.keys()) {
console.log(key);
}
for (let key of data.keys()) {
console.log(key);
}
本例输出了如下内容:
0
1
2
1234
5678
9012
"title"
"format"
keys()
迭代器获取了 colors
、tracking
与 data
各自的键,这些键在三个 for-of
循环中被打印出来。对于数组对象来说,只有数值类型索引被打印了,即使你向数组添加了具名属性也依然如此。这与在数组上使用 for-in
循环是不同的,因为 for-in
循环会迭代所有属性而不仅是数值索引。
集合类型的默认迭代器
当 for-of
循环没有显式指定迭代器时,每种集合类型都有一个默认的迭代器供循环使用。values()
方法是数组与 Set
的默认迭代器,而 entries()
方法则是 Map
的默认迭代器。在 for-of
循环中使用集合对象时,这些默认迭代器会让处理更容易一些。作为例子,研究如下代码:
let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ECMAScript 6");
data.set("format", "print");
// same as using colors.values()
for (let value of colors) {
console.log(value);
}
// same as using tracking.values()
for (let num of tracking) {
console.log(num);
}
// same as using data.entries()
for (let entry of data) {
console.log(entry);
}
此处没有指定迭代器,因此默认的迭代器函数会被使用。数组、Set
与 Map
的默认迭代器反映了这些对象是如何被初始化的,于是此代码就输出了如下内容:
"red"
"green"
"blue"
1234
5678
9012
["title", "Understanding ECMAScript 6"]
["format", "print"]
数组与 Set
默认输出了它们的值,而 Map
返回的则是可以直接传给 Map
构造器的数组格式。另一方面,Weak Set
与 Weak Map
并未拥有内置的迭代器,使用弱引用意味着无法获知这些集合内部到底有多少个值,同时意味着没有方法可以迭代这些值。
解构与
此代码中的 |
字符串的迭代器
从 ES5
发布开始,JS
的字符串就慢慢变得越来越像数组。例如 ES5
标准化了字符串的方括号表示法,用于访问其中的字符(即:使用 text[0]
来获取第一个字符,以此类推)。不过方括号表示法工作在码元而非字符上,因此它不能被用于正确访问双字节的字符,正如此例所演示的:
var message = "A 𠮷 B";
for (let i=0; i < message.length; i++) {
console.log(message[i]);
}
此代码使用了方括号表示法与 length
属性来迭代字符串并打印字符,该字符串包含一个 Unicode
字符,输出结果有点出人意料:
A
(blank)
(blank)
(blank)
(blank)
B
// (blank) 代表空行
由于双字节字符被当作两个分离的码元来对待,此处的输出在 A
与 B
之间就有了四个空行。
幸好,ES6
旨在为 Unicode
提供完全支持(详见第二章),字符串的默认迭代器就是解决字符串迭代问题的一种尝试。这样一来,借助字符串默认迭代器就能处理字符而不是码元。将上个范例修改为使用字符串默认迭代器配合 for-of
循环,会得到更加合适的输出。以下是调整之后的代码:
var message = "A 𠮷 B";
for (let c of message) {
console.log(c);
}
此代码输出了如下内容:
A
(blank)
𠮷
(blank)
B
作用对象是字符,让本次的结果更符合预期:循环成功地打印出了这个 Unicode
字符以及其余字符。
NodeList 的迭代器
文档对象模型(DOM
)具有一种 NodeList
类型,用于表示页面文档中元素的集合。对于需要书写在浏览器中运行的 JS
代码的开发者,要理解 NodeList
对象与数组之间的差异总是稍有困难。NodeList
对象与数组都使用了 length
属性来标明项的数量,并且都使用方括号表示法来访问各个项。然而本质上来说,NodeList
与数组的行为是完全不同的,这会引发许多混乱。
随着默认迭代器被附加到 ES6
,DOM
关于 NodeList
的规定也包含了一个默认迭代器(此规定在 HTML
规范而非 ES6
规范中),其表现方式与数组的默认迭代器一致。这意味着你可以将 NodeList
用于 for-of
循环,或用于其他使用对象默认迭代器的场合。例如:
var divs = document.getElementsByTagName("div");
for (let div of divs) {
console.log(div.id);
}
此代码调用 getElementsByTagName()
来获取一个包含 document
对象中的所有 <div>
元素的 NodeList
。接下来 for-of
循环迭代了每个元素并打印出它们的 ID
,实际上这段代码与在标准数组上使用时并无二致。