在ECMAScript5(简称ES5)中,有三个循环。在2015年6月发布的ECMAScript6(简称ES6)中,新增了一种循环类型。他们是:forforinforeachforof 今天,就让我们一起来看看这4个for循环。1、简单的for循环 我们来看看最常见的写法:constarr〔1,2,3〕;for(leti0;iarr。i){console。log(arr〔i〕);} 当循环中数组的长度没有变化时,我们应该将数组的长度存储在一个变量中,这样效率会更高。下面是改进的写法:constarr〔1,2,3〕;for(leti0,lenarr。i){console。log(arr〔i〕);}2、forin 2。1、使用forin 通常,我们可以使用forin来遍历数组的内容,代码如下:constarr〔1,2,3〕;for(indexinarr){console。log(arr〔index〕arr〔index〕);} 一般来说,操作的结果如下:arr〔0〕1arr〔1〕2arr〔2〕3 但这样做往往会产生问题。 2。2、forin的真相 forin循环遍历对象的属性,而不是数组的索引。所以forin遍历的对象不限于数组,也可以遍历对象。示例如下:constperson{fname:san,lname:zhang,age:99};for(infoinperson){console。log(person〔info〕person〔info〕);} 结果如下:person〔fname〕sanperson〔lname〕zhangperson〔age〕99 需要注意的是forin遍历属性的顺序是不确定的,即输出结果的顺序与对象中属性的顺序无关,也与属性的字母顺序无关,也没有任何其他顺序。 2。3、关于数组的真相 数组是Javascript中的一个对象,Array的索引是属性名。事实上,Javascript中的数组有点误导。 Javascript中的数组与大多数其他语言中的数组不同。首先,Javascript中的数组在内存中不是连续的。 其次,Array的索引不是指偏移量。其实Array的索引不是Number类型,而是String类型。之所以能正确使用arr〔0〕之类的写法,是因为语言可以自动改变Number类型。0转换为String类型的0。 因此,Javascript中从来没有Array索引,只有0、1等属性。 有趣的是,每个Array对象都有一个length属性,这使得它的行为更像其他语言中的数组。 但是为什么遍历Array对象的时候不输出length属性呢?那是因为forin只能遍历可枚举属性,length是不可枚举属性,实际上Array对象还有很多其他不可枚举属性。 现在,让我们回过头来看看使用forin循环数组的例子。我们修改前面遍历数组的例子:constarr〔1,2,3〕;arr。nameHfor(indexinarr){console。log(arr〔index〕arr〔index〕);} 操作的结果是:arr〔0〕1arr〔1〕2arr〔2〕3arr〔name〕Helloworld 我们看到forin遍历我们新的name属性,因为forin遍历对象的所有属性,而不仅仅是索引。 同时,需要注意的是,这里输出的索引值,即0、1、2不是Number类型,而是String类型,因为它们是作为属性输出的,不是索引,这是否意味着我们只能输出数组的内容,而不能向我们的Array对象添加新属性?答案是否定的。 因为forin不仅遍历数组本身的属性,还会遍历数组原型链上的所有可枚举属性。让我们看一个例子:Array。prototype。fatherNameFconstarr〔1,2,3〕;arr。nameHfor(indexinarr){console。log(arr〔index〕arr〔index〕);} 操作的结果是:arr〔0〕1arr〔1〕2arr〔2〕3arr〔name〕Helloworldarr〔fatherName〕Father 至此,我们可以发现forin并不适合遍历Array中的元素,它更适合遍历对象的属性,这也是它创建的初衷。有一个例外,那就是稀疏数组,阅读以下示例:constarr〔〕;arr〔0〕a;arr〔100〕b;arr〔10000〕c;for(keyinarr){if(arr。hasOwnProperty(key)〔19〕d。test(key)key4294967294){console。log(arr〔key〕);}} Forin仅遍历现有实体。上例中forin遍历了3次(分别遍历属性为0、100、10000的元素,普通for循环会遍历10001次)。因此,只要处理得当,forin也可以在遍历Array中的元素方面发挥巨大的作用。 为了避免重复工作,我们可以包装上面的代码:functionarrayHasOwnIndex(array,prop){returnarray。hasOwnProperty(prop)〔19〕d。test(prop)prop4294967294;22} 用法示例如下:for(letkeyinarr){if(arrayHasOwnIndex(arr,key)){console。log(arr〔key〕);}} 2。4、forin性能 如上所述,每次迭代操作都会同时搜索实例或原型属性。forin循环的每次迭代都会产生更多的开销,所以它比其他循环类型慢,一般速度是其他循环类型的17。 因此,除非您明确需要迭代具有未知数量属性的对象,否则您应该避免使用forin循环。如果需要遍历有限数量的已知属性列表,使用其他循环会更快,例如以下示例:constobj{prop1:value1,prop2:value2};constprops〔prop1,prop2〕;for(leti0;iprops。i){console。log(obj〔props〔i〕〕);} 在上面的代码中,对象的属性存储在一个数组中。与forin搜索每个属性相比,代码只关注给定的属性,节省了循环的开销和时间。3、forEach 在ES5中,引入了一个新循环,即forEach循环。constarr〔1,2,3〕;arr。forEach((data){console。log(data);}); 操作结果:123 forEach方法对数组中包含有效值的每一项执行一次回调函数,那些已经被删除(使用delete方法等)或从未赋值的项将被跳过(不包括那些未定义的项)或空值)。回调函数会依次传入三个参数:数组中当前项的值;当前项在数组中的索引;数组对象本身; 需要注意的是,forEach遍历的范围会在第一次调用回调之前确定。调用forEach后添加到数组的项目不会被回调访问。 如果现有值发生变化,则传递给callback的值就是forEach遍历它们时的值。不会遍历已删除的项目。constarr〔〕;arr〔0〕a;arr〔3〕b;arr〔10〕c;arr。nameHarr。forEach((data,index,array){console。log(data,index,array);}); 操作结果:a0〔a,3:b,10:c,name:Helloworld〕b3〔a,3:b,10:c,name:Helloworld〕c10〔a,3:b,10:c,name:Helloworld〕 这里的索引是Number类型的,不会像forin那样遍历原型链上的属性。 因此,在使用forEach时,我们不需要特别声明索引和要遍历的元素,因为这些都是作为回调函数的参数。 另外,forEach会遍历数组中的所有元素,但是ES5定义了一些其他有用的方法,下面是一部分:every:循环在第一次返回false后返回some:循环在第一次返回true后返回filter:返回一个元素满足回调函数的新数组map:在返回之前处理原始数组中的元素reduce:依次处理数组中的元素,将上一次处理的结果作为下一次处理的输入,最终得到最终结果。 forEach性能 您可以看看jsPerf。在不同浏览器下测试的结果是forEach没有for快。如果将测试代码放在控制台中,可能会得到不同的结果。主要原因是控制台的执行环境与真实的代码执行环境不同。4、forof 我们先来看一个例子:constarr〔‘a’,‘b’,‘c’〕;for(letdataofarr){console。log(data);} 运行的结果是:abc 为什么要引入forof? 要回答这个问题,我们先来看看ES6之前的3种for循环的缺陷: forEach不能中断和返回; forin的劣势更加明显。它不仅遍历数组中的元素,还遍历自定义属性,甚至访问原型链上的属性。此外,遍历数组元素的顺序可以是随机的。 所以,针对以上缺点,我们需要对原来的for循环进行改进。但是ES6不会破坏您已经编写的JS代码。 目前,数以千计的网站依赖于forin循环,其中一些甚至将其用于数组遍历。通过修复forin循环来添加数组遍历支持会使这一切变得更加混乱,因此标准委员会在ES6中添加了一个新的循环语法来解决当前的问题forof。 那么forof能做什么呢? 与forEach相比,它可以正确响应break、continue、return。 forof循环不仅支持数组,还支持大多数类似数组的对象,例如DOM节点列表对象。 forof循环还支持字符串遍历,它将字符串作为Unicode字符序列进行迭代。 forof还支持Map和Set(都是ES6中的新功能)对象遍历。 总结一下,forof循环具有以下特点: 这是迭代数组元素的最简洁直接的语法。 这种方法避免了forin循环的所有陷阱。 与forEach不同,它正确响应break、continue和return语句。 它不仅可以遍历数组,还可以遍历类数组对象和其他可迭代对象。 然而,应该注意的是,forof循环不支持普通对象,但是如果您想遍历一个对象的属性,您可以使用forin循环(它就是这样做的)。 最后,但并非最不重要的是,ES6引入了另一种方法来迭代数组的值,那就是Iterator。最后一个例子:constarr〔‘a’,‘b’,‘c’〕;constiterarr〔Symbol。iterator〕();iter。next(){value:‘a’,done:false}iter。next(){value:‘b’,done:false}iter。next(){value:‘c’,done:false}iter。next(){value:undefined,done:true} 不过,这个内容超出了本文的范围,Iterator有很多要讲的。 End