遍历数组的四种方法对比:for vs for-in vs forEach() vs for-of(译文)
作者:Dr. Axel Rauschmayer 原文网址:https://2ality.com/2021/01/looping-over-arrays.html
本文主要比较遍历数组的四种方法:
for
循环
for (let index=0; index < someArray.length; index++) { const elem = someArray[index]; // ···}
for-in
循环
for (const key in someArray) { console.log(key);}
.forEach()
循环
someArray.forEach((elem, index) => { console.log(elem, index);});
for-of
循环
for (const elem of someArray) { console.log(elem);}
以上四种遍历方式,for-of
大部分情况下是最佳选择,我们具体分析一下原因。
[TOC]
1 for循环[ES1]
for
循环出现的比较早,在ECMAScript 1就存在了。for
循环可以遍历每个数组元素的索引和值:
const arr = ['a', 'b', 'c'];arr.prop = 'property value';
for (let index=0; index < arr.length; index++) { const elem = arr[index]; console.log(index, elem);}
// Output:// 0, 'a'// 1, 'b'// 2, 'c'
这种循环方式有什么优缺点呢?
- 首先它非常通用,但是当我们只想循环一个数组时,这种方式显的特别冗余。
- 这种方式允许我们可以不用从第一个元素开始循环,其它循环方式就做不到这一点。
2 for-in循环[ES1]
for-in
循环和for
循环一样,也是在ECMAScript 1就存在了。for-in
遍历的是数组元素的索引:
const arr = ['a', 'b', 'c'];arr.prop = 'property value';
for (const key in arr) { console.log(key);}
// Output:// '0'// '1'// '2'// 'prop'
最好不要用for-in
来遍历数组:
- 这种方式遍历出来的是属性键,而不是值。
- 它遍历的属性键即数组元素的索引是字符串,不是数字(关于数组元素工作原理的更多信息)
- 它遍历所有可枚举的属性键(包括自身的和继承的),而不仅仅是那些数组元素。
for-in
访问继承属性确实有一个场景: 遍历对象所有可枚举属性。但即使这样,我也更喜欢手动迭代原型链,因为可以更好的把控。
3 数组方法.forEach()[ES5]
for
和for-in
方法在遍历数组时不是特别适合,在ECMAScript 5里面提供了一个方法:
Array.prototype.forEach()
const arr = ['a', 'b', 'c'];arr.prop = 'property value';
arr.forEach((elem, index) => { console.log(elem, index);});
// Output:// 'a', 0// 'b', 1// 'c', 2
这个方法可以很方便地让我们获取数组的元素和元素,而且有了箭头函数(ES6规范引入),forEach方法写起来更优雅了。
.froEach()
主要的缺点是:
- 这个方法循环体里面不能用
await
- 在
for
循环里面,我们可以用break
提前结束循环,在这个方法中无法实现。
3.1 在.forEach()中结束循环的替代方法
在我们使用使用像.foreach()
循环方法并提前结束循环,有一个替代方法:.some()
也会遍历所有数组元素,如果它的回调返回值为true就会结束循环。
const arr = ['red', 'green', 'blue'];arr.some((elem, index) => { if (index >= 2) { return true; // break from loop } console.log(elem); // This callback implicitly returns `undefined`, which // is a falsy value. Therefore, looping continues.});
// Output:// 'red'// 'green'
但是,这是对.some()
的滥用,上面这段代码理解起来有点绕(跟for-of和break相比的话)。
4 for-of循环[ES6]
在ECMAScript 6中新增了for-of
循环:
const arr = ['a', 'b', 'c'];arr.prop = 'property value';
for (const elem of arr) { console.log(elem);}// Output:// 'a'// 'b'// 'c'
for-of
才是真正的用来遍历数组的:
- 它遍历数组元素
- 在循环体中可以使用
await
- 如果你需要迁移到到
for-await-of
模式,是很方便的。
- 如果你需要迁移到到
- 即使对外部作用域,我们也可以使用
break
和continue
4.1 for-of & 循环对象
使用for-of
的话,还有个优势,我们不仅可以遍历数组,也可以遍历对象,下面用Map举个例子:
const myMap = new Map() .set(false, 'no') .set(true, 'yes');for (const [key, value] of myMap) { console.log(key, value);}
// Output:// false, 'no'// true, 'yes'
遍历myMap
键值对[key, value]时,我们可以直接可以解构它,来访问key和value。
4.2 for-of & 数组索引
数组方法.keys()
返回一个索引数组:
const arr = ['chocolate', 'vanilla', 'strawberry'];
for (const index of arr.keys()) { console.log(index);}// Output:// 0// 1// 2
4.3 for-of & 数组[index, value]键值对
数组方法.entries()
返回一个基于[index, value]键值对的可迭代对象。我们使用for-of
去解构这个方法,就可以方便地访问索引和值:
const arr = ['chocolate', 'vanilla', 'strawberry'];
for (const [index, value] of arr.entries()) { console.log(index, value);}// Output:// 0, 'chocolate'// 1, 'vanilla'// 2, 'strawberry'
5 结论
综上所述,for-of
比for
,for-in
,.forEach()
在可用性方面更友好。
以上四种循环机制之间的性能差异几乎可以忽略。如果你正在做一些计算密集的工作,对性能有所要求,采用WebAssembly
更合适。
对于更多JavaScript
的内容可以去读一下作者Dr. Axel Rauschmayer的开源书籍《JavaScript for impatient programmers》!本书的亮点:
本书始终关注最新的技术,从而降低初学者学习JavaScript
的难度。