ES6 深入 是关于 ECMAScript 标准第六版(简称 ES6)中添加的 JavaScript 编程语言新功能的系列文章。
你如何遍历数组中的元素?二十年前 JavaScript 推出的时候,你可以这样做
<pre>
for (var index = 0; index < myArray.length; index++) {
console.log(myArray[index]);
}
</pre>
自 ES5 以来,你可以使用内置的 forEach
方法
<pre>
myArray.forEach(function (value) {
console.log(value);
});
</pre>
});
这代码稍微短一些,但有一个小缺点:你不能使用 break
语句退出此循环,也不能使用 return
语句从封闭函数中返回。
如果有一个 for
循环语法专门用来遍历数组元素,那该多好。
<pre>
如何使用 for
–in
循环?
console.log(myArray[index]);
}
</pre>
for (var index in myArray) { // 不要这样做
- }
- 这样做是不可取的,原因如下:
- 此代码中分配给
index
的值是字符串"0"
、"1"
、"2"
等等,而不是实际数字。由于你可能不想要字符串运算("2" + 1 == "21"
),所以这样做充其量是不方便的。
循环体不仅会为数组元素执行,还会为其他人可能添加的任何其他 扩展 属性执行。例如,如果你的数组有一个可枚举属性 myArray.name
,则此循环将多执行一次,其中 index == "name"
。甚至数组原型链上的属性也会被访问。
最令人惊讶的是,在某些情况下,此代码可以以任意顺序遍历数组元素。
简而言之,for
–in
旨在用于具有字符串键的普通 Object
。对于 Array
来说,它不是很好用。
强大的 for-of 循环
<pre>
还记得上周我说过 ES6 不会破坏你已经编写的 JS 代码吗?好吧,数百万个网站依赖于 for
–in
的行为,是的,即使是在数组上的行为。因此,绝不可能“修复”for
–in
使其在用于数组时更方便。ES6 改善这种情况的唯一途径是添加某种新的循环语法。
console.log(value);
}
</pre>
它就是这个样子
- for (var value of myArray) {
- }
- 嗯,经过了那么多铺垫,它看起来并不那么令人印象深刻,是吗?好吧,我们会看看
for
–of
是否有什么巧妙的技巧。现在,只需注意
这是迄今为止最简洁、最直接的遍历数组元素的语法
它避免了 for
–in
的所有陷阱
与 forEach()
不同,它支持 break
、continue
和 return
for
–in
循环用于遍历对象属性。
for
–of
循环用于遍历数据,比如数组中的值。
但不止这些。
<pre>
其他集合也支持 for-offor
–of
不仅适用于数组。它还适用于大多数类似数组的对象,比如 DOM NodeList
。
}
</pre>
它还适用于字符串,将字符串视为 Unicode 字符序列
for (var chr of "😺😲") {
alert(chr);
<pre>
}
它还适用于 Map
和 Set
对象。
</pre>
哦,抱歉。你以前没听说过 Map
和 Set
对象?好吧,它们是 ES6 中的新功能。我们会在以后的文章中专门介绍它们。如果你在其他语言中使用过映射和集合,那么你不会感到陌生。
<pre>
例如,Set
对象适用于消除重复
// 从单词数组创建集合
}
</pre>
var uniqueWords = new Set(words);
<pre>
创建了 Set
后,你可能想遍历其内容。很简单
for (var word of uniqueWords) {
}
</pre>
console.log(word);
}
Map
有点不同:它内部的数据由键值对组成,因此你需要使用解构将键和值解包到两个单独的变量中
<pre>
for (var [key, value] of phoneBookMap) {
console.log(key + "'s phone number is: " + value);
}
}
</pre>
解构是 ES6 中的另一个新功能,也是以后博客文章的热门话题。我应该把这些记下来。
现在,你应该明白了:JS 已经有很多不同的集合类,并且还有更多正在开发中。for
–of
被设计为用于所有这些集合的通用循环语句。
for
–of
不适用于普通 Object
,但是如果你想遍历对象的属性,可以使用 for
–in
(这就是它的用途)或内置的 Object.keys()
// 将对象的自身可枚举属性转储到控制台
for (var key of Object.keys(someObject)) {
console.log(key + ": " + someObject[key]);
}
幕后
<pre>
“好的艺术家会模仿,伟大的艺术家会偷窃。” — 巴勃罗·毕加索
ES6 中一个贯穿始终的主题是,添加到语言中的新功能并非凭空出现。大多数功能都曾在其他语言中尝试过并证明了其有用性。
例如,for
–of
循环类似于 C++、Java、C# 和 Python 中的类似循环语句。与它们一样,它适用于语言及其标准库提供的多种不同的数据结构。但它也是语言的扩展点。
与这些其他语言中的 for
/foreach
语句一样,for
–of
完全依赖于方法调用来实现。Array
、Map
、Set
和我们前面提到的其他对象都有一个共同点,就是它们都有一个迭代器方法。
</pre>
还有另一种对象也可以具有迭代器方法:任何你想要的。
正如你可以向任何对象添加 myObject.toString()
方法,这样 JS 就会知道如何将该对象转换为字符串一样,你也可以向任何对象添加 myObject[Symbol.iterator]()
方法,这样 JS 就会知道如何遍历该对象。
例如,假设你正在使用 jQuery,虽然你非常喜欢 .each()
,但你希望 jQuery 对象也支持 for
–of
。以下是如何做到这一点
// 由于 jQuery 对象类似于数组,
// 所以给它们赋予与数组相同的迭代器方法
jQuery.prototype[Symbol.iterator] =
<pre>
Array.prototype[Symbol.iterator];
好的,我知道你在想什么。那个 [Symbol.iterator]
语法看起来很奇怪。这到底是怎么回事?这与方法的名称有关。标准委员会本可以简单地将此方法命名为 .iterator()
,但是,你的现有代码中可能已经有一些对象具有 .iterator()
方法,这会造成很大的混乱。因此,标准使用符号而不是字符串作为此方法的名称。
符号是 ES6 中的新功能,我们会在以后的文章中详细介绍它们。现在,你只需要知道标准可以定义一个全新的符号,比如 Symbol.iterator
,并且保证它不会与任何现有代码冲突。代价是语法有点奇怪。但这对于这个多功能的新功能和出色的向后兼容性来说是一个小代价。
},
具有 [Symbol.iterator]()
方法的对象称为可迭代对象。在接下来的几周内,我们将看到可迭代对象的概念在整个语言中都有应用,不仅在 for
–of
中,还包括在 Map
和 Set
构造函数、解构赋值和新的扩展运算符中。
迭代器对象
}
};
</pre>
每次调用此.next()
方法时,它都返回相同的结果,告诉for
-of
循环 (a) 我们尚未完成迭代;以及 (b) 下一个值为0
。这意味着for (value of zeroesForeverIterator) {}
将是一个无限循环。当然,典型的迭代器不会像这样简单。
这种迭代器设计,其.done
和.value
属性,在表面上与其他语言中迭代器的运作方式不同。在 Java 中,迭代器具有独立的.hasNext()
和.next()
方法。在 Python 中,它们只有一个.next()
方法,在没有更多值时抛出StopIteration
。但所有这三种设计在根本上返回相同的信息。
迭代器对象还可以实现可选的.return()
和.throw(exc)
方法。如果循环由于异常或break
或return
语句而提前退出,for
-of
循环将调用.return()
。如果迭代器需要执行一些清理操作或释放它正在使用的资源,它可以实现.return()
。大多数迭代器对象不需要实现它。.throw(exc)
则是一个更加特殊的情况:for
-of
根本不会调用它。但我们下周将更多地了解它。
现在我们已经掌握了所有细节,我们可以使用一个简单的for
-of
循环,并根据底层方法调用对其进行重写。
首先是for
-of
循环
<pre>
for (VAR of ITERABLE) {
STATEMENTS
}
</pre>
这是一个粗略的等效代码,使用底层方法和一些临时变量
<pre>
var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
VAR = $result.value;
STATEMENTS
$result = $iterator.next();
}
</pre>
此代码没有展示.return()
是如何处理的。我们可以添加它,但我认为这会掩盖正在发生的事情,而不是阐明它。for
-of
易于使用,但幕后有很多工作要做。
我什么时候可以使用它?
for
-of
循环在所有当前的 Firefox 版本中都受支持。如果您转到chrome://flags
并启用“实验性 JavaScript”,它将在 Chrome 中受支持。它也适用于微软的 Spartan 浏览器,但不能在 IE 的发布版本中使用。如果您想在网络上使用这种新语法,但需要支持 IE 和 Safari,您可以使用像Babel或 Google 的Traceur这样的编译器,将您的 ES6 代码转换为 Web 友好的 ES5 代码。
在服务器上,您不需要编译器——您今天就可以在 io.js(以及 Node,使用--harmony
选项)中开始使用for
-of
。
(更新: 此前忽略了在 Chrome 中默认情况下禁用了for
-of
。感谢 Oleg 在评论中指出了这个错误。)
{done: true}
呼!
好了,我们今天就到这儿了,但我们还没有完成for
-of
循环。
ES6 中还有一种新的对象类型,它与for
-of
配合得很好。我没有提到它,因为它将是下周文章的主题。我认为这个新特性是 ES6 中最神奇的东西。如果您之前没有在 Python 和 C# 等语言中遇到它,您一开始可能会觉得它令人难以置信。但它是编写迭代器最简单的方法,它在重构中很有用,并且可能会改变我们编写异步代码的方式,无论是在浏览器中还是在服务器上。所以,下周让我们深入探讨一下 ES6 生成器。
26 条评论