关于for of循环的用法和使用

强大的for-of循环

还记得在《深入浅出ES6(一):ES6是什么》中我向你们承诺过的话么?ES6不会破坏你已经写好的JS代码。目前看来,成千上万的Web网站依赖for-in循环,其中一些网站甚至将其用于数组遍历。如果想通过修正for-in循环增加数组遍历支持会让这一切变得更加混乱,因此,标准委员会在ES6中增加了一种新的循环语法来解决目前的问题。

就像这样:


for (var value of myArray) {
  console.log(value);
}

是的,与之前的内建方法相比,这种循环方式看起来是否有些眼熟?那好,我们将要探究一下for-of循环的外表下隐藏着哪些强大的功能。现在,只需记住:

  • 这是最简洁、最直接的遍历数组元素的语法
  • 这个方法避开了for-in循环的所有缺陷
  • 与forEach()不同的是,它可以正确响应break、continue和return语句

for-in循环用来遍历对象属性。

for-of循环用来遍历数据—例如数组中的值。

但是,不仅如此!

for-of循环也可以遍历其它的集合

for-of循环不仅支持数组,还支持大多数类数组对象,例如DOM NodeList对象

for-of循环也支持字符串遍历,它将字符串视为一系列的Unicode字符来进行遍历:


for (var chr of "") {
  alert(chr);
}

它同样支持Map和Set对象遍历。

对不起,你一定没听说过Map和Set对象。他们是ES6中新增的类型。我们将在后续的文章讲解这两个新的类型。如果你曾在其它语言中使用过Map和Set,你会发现ES6中的并无太大出入。

举个例子,Set对象可以自动排除重复项:


// 基于单词数组创建一个set对象
var uniqueWords = new Set(words);

生成Set对象后,你可以轻松遍历它所包含的内容:


for (var word of uniqueWords) {
   console.log(word);
}

Map对象稍有不同:内含的数据由键值对组成,所以你需要使用解构(destructuring)来将键值对拆解为两个独立的变量:


for (var [key, value] of phoneBookMap) {
   console.log(key + "‘s phone number is: " + value);
}

解构也是ES6的新特性,我们将在另一篇文章中讲解。看来我应该记录这些优秀的主题,未来有太多的新内容需要一一剖析。

现在,你只需记住:未来的JS可以使用一些新型的集合类,甚至会有更多的类型陆续诞生,而for-of就是为遍历所有这些集合特别设计的循环语句。

for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用for-in循环(这也是它的本职工作)或内建的Object.keys()方法:


// 向控制台输出对象的可枚举属性
for (var key of Object.keys(someObject)) {
  console.log(key + ": " + someObject[key]);
}

深入理解

“能工摹形,巧匠窃意。”——巴勃罗·毕卡索

ES6始终坚持这样的宗旨:凡是新加入的特性,势必已在其它语言中得到强有力的实用性证明。

举个例子,新加入的for-of循环像极了C++、Java、C#以及Python中的循环语句。与它们一样,这里的for-of循环支持语言和标准库中提供的几种不同的数据结构。它同样也是这门语言中的一个扩展点(译注:关于扩展点,建议参考 1. 浅析扩展点 2. What are extensions and extension points?)。

正如其它语言中的for/foreach语句一样,for-of循环语句通过方法调用来遍历各种集合。数组、Maps对象、Sets对象以及其它在我们讨论的对象有一个共同点,它们都有一个迭代器方法。

你可以给任意类型的对象添加迭代器方法。

当你为对象添加myObject.toString()方法后,就可以将对象转化为字符串,同样地,当你向任意对象添加myObject[Symbol.iterator]()方法,就可以遍历这个对象了。

举个例子,假设你正在使用jQuery,尽管你非常钟情于里面的.each()方法,但你还是想让jQuery对象也支持for-of循环,你可以这样做:


// 因为jQuery对象与数组相似
// 可以为其添加与数组一致的迭代器方法
jQuery.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

好的,我知道你在想什么,那个[Symbol.iterator]语法看起来很奇怪,这段代码到底做了什么呢?这里通过Symbol处理了一下方法的名称。标准委员会可以把这个方法命名为.iterator()方法,但是如果你的代码中的对象可能也有一些.iterator()方法,这一定会让你感到非常困惑。于是在ES6标准中使用symbol来作为方法名,而不是使用字符串。

你大概也猜到了,Symbols是ES6中的新类型,我们会在后续的文章中讲解。现在,你需要记住,基于新标准,你可以定义一个全新的symbol,就像Symbol.iterator,如此一来可以保证不与任何已有代码产生冲突。这样做的代价是,这段代码的语法看起来会略显生硬,但是这微乎其微代价却可以为你带来如此多的新特性和新功能,并且你所做的这一切可以完美地向后兼容。

所有拥有[Symbol.iterator]()的对象被称为可迭代的。在接下来的文章中你会发现,可迭代对象的概念几乎贯穿于整门语言之中,不仅是for-of循环,还有Map和Set构造函数、解构赋值,以及新的展开操作符。

迭代器对象

现在,你将无须亲自从零开始实现一个对象迭代器,我们会在下一篇文章详细讲解。为了帮助你理解本文,我们简单了解一下迭代器(如果你跳过这一章,你将错过非常精彩的技术细节)。

for-of循环首先调用集合的[Symbol.iterator]()方法,紧接着返回一个新的迭代器对象。迭代器对象可以是任意具有.next()方法的对象;for-of循环将重复调用这个方法,每次循环调用一次。举个例子,这段代码是我能想出来的最简单的迭代器:


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

每一次调用.next()方法,它都返回相同的结果,返回给for-of循环的结果有两种可能:(a) 我们尚未完成迭代;(b) 下一个值为0。这意味着(value of zeroesForeverIterator) {}将会是一个无限循环。当然,一般来说迭代器不会如此简单。

这个迭代器的设计,以及它的.done和.value属性,从表面上看与其它语言中的迭代器不太一样。在Java中,迭代器有分离的.hasNext()和.next()方法。在Python中,他们只有一个.next() 方法,当没有更多值时抛出StopIteration异常。但是所有这三种设计从根本上讲都返回了相同的信息。

迭代器对象也可以实现可选的.return()和.throw(exc)方法。如果for-of循环过早退出会调用.return()方法,异常、break语句或return语句均可触发过早退出。如果迭代器需要执行一些清洁或释放资源的操作,可以在.return()方法中实现。大多数迭代器方法无须实现这一方法。.throw(exc)方法的使用场景就更特殊了:for-of循环永远不会调用它。但是我们还是会在下一篇文章更详细地讲解它的作用。

现在我们已了解所有细节,可以写一个简单的for-of循环然后按照下面的方法调用重写被迭代的对象。

首先是for-of循环:


for (VAR of ITERABLE) {
  一些语句
}

然后是一个使用以下方法和少许临时变量实现的与之前大致相当的示例,:


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

这段代码没有展示.return()方法是如何处理的,我们可以添加这部分代码,但我认为这对于我们正在讲解的内容来说过于复杂了。for-of循环用起来很简单,但是其背后有着非常复杂的机制。

我何时可以开始使用这一新特性?

目前,对于for-of循环新特性,所有最新版本Firefox都(部分)支持(译注:从FF 13开始陆续支持相关功能,FF 36 - FF 40基本支持大部分特性),在Chrome中可以通过访问 chrome://flags 并启用“实验性JavaScript”来支持。微软的Spartan浏览器支持,但是IE不支持。如果你想在web环境中使用这种新语法,同时需要支持IE和Safari,你可以使用Babel或Google的Traceur这些编译器来将你的ES6代码翻译为Web友好的ES5代码。

而在服务端,你不需要类似的编译器,io.js中默认支持ES6新语法(部分),在Node中需要添加--harmony选项来启用相关特性。

{done: true}

喲!

好的,我们今天的讲解就到这里,但是对于for-of循环的使用远没有结束。

在ES6中有一种新的对象与for-of循环配合使用非常契合,我没有提及它因为它是我们下周文章的主题,我认为这种新特性是ES6种最梦幻的地方,如果你尚未在类似Python和C#的语言中遇到它,你一开始很可能会发现它令人难以置信,但是这是编写迭代器最简单的方式,在重构中非常有用,并且它很可能改变我们书写异步代码的方式,无论是在浏览器环境还是服务器环境,所以,下周的深入浅出 ES6 中,请务必一起来仔细看看 ES6 的生成器:generators。

时间: 2024-12-26 04:11:58

关于for of循环的用法和使用的相关文章

Redis源码分析(二十三)--- CRC循环冗余算法和RAND随机数算法

今天开始研究Redis源码中的一些工具类的代码实现,工具类在任何语言中,实现的算法原理应该都是一样的,所以可以借此机会学习一下一些比较经典的算法.比如说我今天看的Crc循环冗余校验算法和rand随机数产生算法. CRC算法全称循环冗余校验算法.CRC校验的基本思想是利用线性编码理论,在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的监督码(既CRC码)r位,并附在信息后边,构成一个新的二进制码序列数共(k+r)位,最后发送出去.在接收端, 则根据信息码和CRC码之间所遵循的规则进

ios中for循环和for-in循环的用法和区别

for循环主要用在你已经知道了循环的次数 比如你要在屏幕上打出100个"hello word!" 那么就可以用for (int i=0;i<100,i++) 这里你已经很明确了是100 for in循环主要是方便遍历已经知道对象类型的数组 同时不关心索引的情况 比如已经知道数组里都是uibutton 那么你就可以直接用 for uibutton *tmpbutton in array 这样你就可以直接通过tmpbutton直接访问数组里的每一个button 另外数组还有一种用bl

作业:for循环,迭代法和穷举法

                                                for()循环 四要素:初始条件,循环条件,状态改变,循环体. 执行过程:初始条件--循环条件--循环体--状态改变--循环条件.... 注意:for的小括号里面用分号隔开,for的小括号后不要加分号. for的嵌套. 应用:迭代法,穷举法. 一.迭代法:有一定规律. 每次循环都是从上次运算结果中获得数据,本次运算的结果都是要为下次运算做准备. 第一题:求100以内所有数的和. 第二题:求阶乘!(6的

总结一下几个for循环常见用法和区别

1.for循环//有人喜欢使用一个临时变量把长度缓存起来,说是大数据时效果更好(我本人没有去验证) for(j = 0,len=arr.length; j < len; j++) { //执行代码 } 分析可用功能 break和continue 2.forEach//遍历数组中的每一项,没有返回值,对原数组没有影响,不支持IE//1 没有返回值 arr.forEach((item,index,array)=>{//箭头函数的写法 //执行代码 }) arr.forEach(function (

shell脚本进阶之循环判断

p.MsoNormal,li.MsoNormal,div.MsoNormal { margin: 0cm; margin-bottom: .0001pt; text-align: justify; font-size: 14.0pt; font-family: 等线 } h1 { margin-top: 15.6pt; margin-right: 0cm; margin-left: 0cm; margin-bottom: .0001pt; text-align: justify; line-he

swift 2.0 语法 循环

//: Playground - noun: a place where people can play import UIKit /*: for循环 * 基本用法和OC一致 * 条件表达式必须是bool类型的值 * 条件表达式的()可以省略 * 在OC中如果{}中只有一条语句, 那么{}可以省略, 而Swift不可以 */ for var i = 0; i < 10; i++ { print(i) } /*: 区间 半闭区间: 0..<10  包含头不包含尾 闭区间:   0...10  包

bash脚本编程详细剖析

bash脚本编程详细剖析          背景:bash脚本编程是Linux学习一个至关重要的部分,想完成一个脚本可能很简单:但是想让自己的脚本写的让人觉得心旷神怡实为不简单.bash是所有Linux发行版的几乎都有的,因此我们这里以bash脚本为例,讨论bash脚本的编写方法.对于bash脚本编程中一些比较重要的知识点,我这里也会给予案例演示. 一.脚本编程中前话: 我们都知道,bash脚本编程说白了就是命令的堆积.只不过这种堆积的方式不是杂乱无章的堆积,而是按照一定要求和格式的链接.这说明

java===java基础学习(6)---流程控制,for,if,switch,continue,break

注意点: for循环的用法和python截然不同,注意格式 switch~,switch对应的case每当执行完毕都要break,由于基本不怎么用switch,所以作为了解. 中断流程控制语句,请考虑多重循环的场景,最好用标签做区别. continue条件满足,则程序到最内部循环的首部! //Scanner文本文件读取与处理 package testbotoo; import java.io.File; import java.io.IOException; import java.io.Pri

Shell脚本之进阶

一.条件选择.判断(if.case)1.1 if语句用法及实例当我们在脚本中遇到需要判断的时候,我们就可以用if语句来实现.具体的语法如下: 单分支if 判断条件;then 条件为真的分支代码 fi 双分支if 判断条件; then 条件为真的分支代码 else 条件为假的分支代码 fi 多分支if 判断条件1; then 条件为真的分支代码elif 判断条件2; then 条件为真的分支代码elif 判断条件3; then 条件为真的分支代码 else 以上条件都为假的分支代码fi 在多分支中