首页 web前端 js教程 深入解读JavaScript中的Iterator和for-of循环_基础知识

深入解读JavaScript中的Iterator和for-of循环_基础知识

May 16, 2016 pm 03:48 PM
for iterator javascript

如何遍历一个数组的元素?在 20 年前,当 JavaScript 出现时,你也许会这样做:

for (var index = 0; index < myArray.length; index++) {
 console.log(myArray[index]);
}
 
for (var index = 0; index < myArray.length; index++) {
 console.log(myArray[index]);
}
登录后复制

自从 ES5 开始,你可以使用内置的 forEach 方法:

JavaScript
myArray.forEach(function (value) {
 console.log(value);
});
 
myArray.forEach(function (value) {
 console.log(value);
});
登录后复制

代码更为精简,但有一个小缺点:不能使用 break 语句来跳出循环,也不能使用 return 语句来从闭包函数中返回。

如果有 for- 这种语法来遍历数组就会方便很多。

那么,使用 for-in 怎么样?

for (var index in myArray) { // 实际代码中不要这么做
 console.log(myArray[index]);
}

 
for (var index in myArray) { // 实际代码中不要这么做
 console.log(myArray[index]);
}

登录后复制

这样不好,因为:

上面代码中的 index 变量将会是 "0"、"1"、"3" 等这样的字符串,而并不是数值类型。如果你使用字符串的 index 去参与某些运算("2" + 1 == "21"),运算结果可能会不符合预期。
不仅数组本身的元素将被遍历到,那些由用户添加的附加(expando)元素也将被遍历到,例如某数组有这样一个属性 myArray.name,那么在某次循环中将会出现 index="name" 的情况。而且,甚至连数组原型链上的属性也可能被遍历到。
最不可思议的是,在某些情况下,上面代码将会以任意顺序去遍历数组元素。

简单来说,for-in 设计的目的是用于遍历包含键值对的对象,对数组并不是那么友好。
强大的 for-of 循环

记得上次我提到过,ES6 并不会影响现有 JS 代码的正常运行,已经有成千上万的 Web 应用都依赖于 for-in 的特性,甚至也依赖 for-in 用于数组的特性,所以从来就没有人提出“改善”现有 for-in 语法来修复上述问题。ES6 解决该问题的唯一办法是引入新的循环遍历语法。

这就是新的语法:

for (var value of myArray) {
 console.log(value);
}
 
for (var value of myArray) {
 console.log(value);
}
登录后复制

通过介绍上面的 for-in 语法,这个语法看起来并不是那么令人印象深刻。后面我们将详细介绍for-of 的奇妙之处,现在你只需要知道:

  • 这是遍历数组最简单直接的方法
  • 避免了所有 for–in 语法存在的坑
  • 与 forEach() 不同的是,它支持 break、continue 和 return 语句。

for–in 用于遍历对象的属性。

for-of 用于遍历数据 — 就像数组中的元素。

然而,这还不是 for-of 的所有特性,下面还有更精彩的部分。
支持 for-of 的其他集合

for-of 不仅仅是为数组设计,还可以用于类数组的对象,比如 DOM 对象的集合 NodeList。

也可以用于遍历字符串,它将字符串看成是 Unicode 字符的集合:

201572885849513.jpg (423×77)

它还适用于 Map 和 Set 对象。

也许你从未听说过 Map 和 Set 对象,因为它们是 ES6 中的新对象,后面将有单独的文章去详细介绍它们。如果你在其他语言中使用过这两个对象,那就简单多了。

例如,可以用一个 Set 对象来对数组元素去重:

JavaScript
// make a set from an array of words
var uniqueWords = new Set(words);
 
// make a set from an array of words
var uniqueWords = new Set(words);
登录后复制

当得到一个 Set 对象后,你很可能会去遍历该对象,这很简单:

for (var word of uniqueWords) {
 console.log(word);
}
 
for (var word of uniqueWords) {
 console.log(word);
}
登录后复制

Map 对象由键值对构成,遍历方式略有不同,你需要用两个独立的变量来分别接收键和值:

for (var [key, value] of phoneBookMap) {
 console.log(key + "'s phone number is: " + value);
}
 
for (var [key, value] of phoneBookMap) {
 console.log(key + "'s phone number is: " + value);
}
登录后复制

到目前为止,你已经知道:JS 已经支持一些集合对象,而且后面将会支持更多。for-of 语法正是为这些集合对象而设计。

for-of 不能直接用来遍历对象的属性,如果你想遍历对象的属性,你可以使用 for-in 语句(for-in 就是用来干这个的),或者使用下面的方式:

// dump an object's own enumerable properties to the console
for (var key of Object.keys(someObject)) {
 console.log(key + ": " + someObject[key]);
}

 
// dump an object's own enumerable properties to the console
for (var key of Object.keys(someObject)) {
 console.log(key + ": " + someObject[key]);
}

登录后复制

内部原理

“好的艺术家复制,伟大的艺术家偷窃。” — 巴勃罗·毕加索

被添加到 ES6 中的那些新特性并不是无章可循,大多数特性都已经被使用在其他语言中,而且事实也证明这些特性很有用。

就拿 for-of 语句来说,在 C++、JAVA、C# 和 Python 中都存在类似的循环语句,并且用于遍历这门语言和其标准库中的各种数据结构。

与其他语言中的 for 和 foreach 语句一样,for-of 要求被遍历的对象实现特定的方法。所有的 Array、Map 和 Set 对象都有一个共性,那就是他们都实现了一个迭代器(iterator)方法。

那么,只要你愿意,对其他任何对象你都可以实现一个迭代器方法。

这就像你可以为一个对象实现一个 myObject.toString() 方法,来告知 JS 引擎如何将一个对象转换为字符串;你也可以为任何对象实现一个 myObject[Symbol.iterator]() 方法,来告知 JS 引擎如何去遍历该对象。

例如,如果你正在使用 jQuery,并且非常喜欢用它的 each() 方法,现在你想使所有的 jQuery 对象都支持 for-of 语句,你可以这样做:

// Since jQuery objects are array-like,
// give them the same iterator method Arrays have
jQuery.prototype[Symbol.iterator] =
 Array.prototype[Symbol.iterator];

 
// Since jQuery objects are array-like,
// give them the same iterator method Arrays have
jQuery.prototype[Symbol.iterator] =
 Array.prototype[Symbol.iterator];

登录后复制

你也许在想,为什么 [Symbol.iterator] 语法看起来如此奇怪?这句话到底是什么意思?问题的关键在于方法名,ES 标准委员会完全可以将该方法命名为 iterator(),但是,现有对象中可能已经存在名为“iterator”的方法,这将导致代码混乱,违背了最大兼容性原则。所以,标准委员会引入了 Symbol,而不仅仅是一个字符串,来作为方法名。

Symbol 也是 ES6 的新特性,后面将会有单独的文章来介绍。现在你只需要知道标准委员会引入全新的 Symbol,比如 Symbol.iterator,是为了不与之前的代码冲突。唯一不足就是语法有点奇怪,但对于这个强大的新特性和完美的后向兼容来说,这个就显得微不足道了。

一个拥有 [Symbol.iterator]() 方法的对象被认为是可遍历的(iterable)。在后面的文章中,我们将看到“可遍历对象”的概念贯穿在整个语言中,不仅在 for-of 语句中,而且在 Map和 Set 的构造函数和析构(Destructuring)函数中,以及新的扩展操作符中,都将涉及到。
迭代器对象

通常我们不会完完全全从头开始去实现一个迭代器(Iterator)对象,下一篇文章将告诉你为什么。但为了完整起见,让我们来看看一个迭代器对象具体是什么样的。(如果你跳过了本节,你将会错失某些技术细节。)

就拿 for-of 语句来说,它首先调用被遍历集合对象的 [Symbol.iterator]() 方法,该方法返回一个迭代器对象,迭代器对象可以是拥有 .next 方法的任何对象;然后,在 for-of 的每次循环中,都将调用该迭代器对象上的 .next 方法。下面是一个最简单的迭代器对象:

var zeroesForeverIterator = {
 [Symbol.iterator]: function () {
 return this;
 },
 next: function () {
 return {done: false, value: 0};
 }
};
 
var zeroesForeverIterator = {
 [Symbol.iterator]: function () {
 return this;
 },
 next: function () {
 return {done: false, value: 0};
 }
};
登录后复制

在上面代码中,每次调用 .next() 方法时都返回了同一个结果,该结果一方面告知 for-of语句循环遍历还没有结束,另一方面告知 for-of 语句本次循环的值为 0。这意味着 for (value of zeroesForeverIterator) {} 是一个死循环。当然,一个典型的迭代器不会如此简单。

ES6 的迭代器通过 .done 和 .value 这两个属性来标识每次的遍历结果,这就是迭代器的设计原理,这与其他语言中的迭代器有所不同。在 Java 中,迭代器对象要分别使用 .hasNext()和 .next() 两个方法。在 Python 中,迭代器对象只有一个 .next() 方法,当没有可遍历的元素时将抛出一个 StopIteration 异常。但从根本上说,这三种设计都返回了相同的信息。

迭代器对象可以还可以选择性地实现 .return() 和 .throw(exc) 这两个方法。如果由于异常或使用 break 和 return 操作符导致循环提早退出,那么迭代器的 .return() 方法将被调用,可以通过实现 .return() 方法来释放迭代器对象所占用的资源,但大多数迭代器都不需要实现这个方法。throw(exc) 更是一个特例:在遍历过程中该方法永远都不会被调用,关于这个方法,我会在下一篇文章详细介绍。

现在我们知道了 for-of 的所有细节,那么我们可以简单地重写该语句。

首先是 for-of 循环体:

for (VAR of ITERABLE) {
 STATEMENTS
}
 
for (VAR of ITERABLE) {
 STATEMENTS
}
登录后复制

这只是一个语义化的实现,使用了一些底层方法和几个临时变量:

var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
 VAR = $result.value;
 STATEMENTS
 $result = $iterator.next();
}

 
var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
 VAR = $result.value;
 STATEMENTS
 $result = $iterator.next();
}

登录后复制

上面代码并没有涉及到如何调用 .return() 方法,我们可以添加相应的处理,但我认为这样会影响我们对内部原理的理解。for-of 语句使用起来非常简单,但在其内部有非常多的细节。
兼容性

目前,所有 Firefox 的 Release 版本都已经支持 for-of 语句。Chrome 默认禁用了该语句,你可以在地址栏输入 chrome://flags 进入设置页面,然后勾选其中的 “Experimental JavaScript” 选项。微软的 Spartan 浏览器也支持该语句,但是 IE 不支持。如果你想在 Web 开发中使用该语句,而且需要兼容 IE 和 Safari 浏览器,你可以使用 Babel 或 Google 的 Traceur 这类编译器,来将 ES6 代码转换为 Web 友好的 ES5 代码。

对于服务器端,我们不需要任何编译器 — 可以在 io.js 中直接使用该语句,或者在 NodeJS 启动时使用 --harmony 启动选项。

{done: true}
登录后复制

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

仓库:如何复兴队友
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

解决kernel_security_check_failure蓝屏的17种方法 解决kernel_security_check_failure蓝屏的17种方法 Feb 12, 2024 pm 08:51 PM

Kernelsecuritycheckfailure(内核检查失败)就是一个比较常见的停止代码类型,可蓝屏错误出现不管是什么原因都让很多的有用户们十分的苦恼,下面就让本站来为用户们来仔细的介绍一下17种解决方法吧。kernel_security_check_failure蓝屏的17种解决方法方法1:移除全部外部设备当您使用的任何外部设备与您的Windows版本不兼容时,则可能会发生Kernelsecuritycheckfailure蓝屏错误。为此,您需要在尝试重新启动计算机之前拔下全部外部设备。

如何使用WebSocket和JavaScript实现在线语音识别系统 如何使用WebSocket和JavaScript实现在线语音识别系统 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

WebSocket与JavaScript:实现实时监控系统的关键技术 WebSocket与JavaScript:实现实时监控系统的关键技术 Dec 17, 2023 pm 05:30 PM

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何使用WebSocket和JavaScript实现在线预约系统 如何使用WebSocket和JavaScript实现在线预约系统 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

如何利用JavaScript和WebSocket实现实时在线点餐系统 如何利用JavaScript和WebSocket实现实时在线点餐系统 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

Win10如何卸载Skype for Business?电脑上的skype怎么彻底卸载方法 Win10如何卸载Skype for Business?电脑上的skype怎么彻底卸载方法 Feb 13, 2024 pm 12:30 PM

Win10skype可以卸载吗是很多用户们都想知道的一个问题,因为很多的用户们发现自己电脑上的默认程序上有这个应用,担心删除后会影响到系统的运行,下面就让本站来为用户们来仔细的介绍一下Win10如何卸载SkypeforBusiness吧。Win10如何卸载SkypeforBusiness1、在电脑桌面点击Windows图标,再点击设置图标进入。2、点击“应用”。3、在搜索框中输入“Skype”,点击选中找到的结果。4、点击“卸载”。5

JavaScript和WebSocket:打造高效的实时天气预报系统 JavaScript和WebSocket:打造高效的实时天气预报系统 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

简易JavaScript教程:获取HTTP状态码的方法 简易JavaScript教程:获取HTTP状态码的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

See all articles