在 JavaScript 中循环遍历数组
在 Java 中,可以使用
for
循环遍历数组中的对象,如下所示:
String[] myStringArray = {"Hello", "World"};
for (String s : myStringArray) {
// Do something
}
我可以在 JavaScript 中执行相同操作吗?
三个主要选项:
-
for (var i = 0; i < xs.length; i++) { console.log(xs[i]);
-
xs.forEach((x, i) => console.log(x));
-
for (const x of xs) { console.log(x);
详细示例如下。
1.顺序
for
循环:
var myStringArray = ["Hello","World"];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
console.log(myStringArray[i]);
//Do something
}
优点
- 适用于所有环境
-
您可以使用
break
和continue
流控制语句
缺点
- 太冗长
- 命令式
- 容易出现 off-by-one 错误 (有时也称为 栅栏柱错误 )
2.
Array.prototype.forEach
:
ES5
规范引入了许多有益的数组方法。其中之一,
Array.prototype.forEach
,为我们提供了一种简洁的数组迭代方法:
const array = ["one", "two", "three"]
array.forEach(function (item, index) {
console.log(item, index);
});
从撰写 ES5 规范发布(2009 年 12 月)到现在已经快十年了,几乎所有现代引擎都在桌面、服务器和移动环境中实现了它,所以使用它们是安全的。
而使用 ES6 箭头函数语法,它就更加简洁了:
array.forEach(item => console.log(item));
除非你计划支持古老的平台(例如, Internet Explorer 11 );您也可以安全使用。
优点
- 非常简短和简洁。
- 声明式
缺点
-
不能使用
break
/continue
通常,您可以通过在迭代数组元素之前对其进行过滤来代替
break
命令式循环的需要,例如:
array.filter(item => item.condition < 10)
.forEach(item => console.log(item))
请记住,如果您正在迭代数组
以从中构建另一个数组
,则应使用
map
。我已经多次见过这种反模式了。
反模式:
const numbers = [1,2,3,4,5], doubled = [];
numbers.forEach((n, i) => { doubled[i] = n * 2 });
map 的正确用例:
const numbers = [1,2,3,4,5];
const doubled = numbers.map(n => n * 2);
console.log(doubled);
此外,如果您尝试将数组 reduce 为一个值,例如,您想要对数字数组求和,则应使用 reduce 方法。
反模式:
const numbers = [1,2,3,4,5];
const sum = 0;
numbers.forEach(num => { sum += num });
reduce 的正确用法:
const numbers = [1,2,3,4,5];
const sum = numbers.reduce((total, n) => total + n, 0);
console.log(sum);
3. ES6
for-of
语句:
ES6
标准引入了可迭代对象的概念,并定义了一种用于遍历数据的新构造,即
for...of
语句。
此语句适用于任何类型的可迭代对象,也适用于生成器(具有
\[Symbol.iterator\]
属性的任何对象)。
数组对象在 ES6 中定义为内置可迭代对象,因此您可以对其使用此语句:
let colors = ['red', 'green', 'blue'];
for (const color of colors){
console.log(color);
}
优点
- 它可以迭代各种各样的对象。
-
可以使用普通流控制语句(
break
/continue
)。 - 可用于迭代串行异步值。
缺点
- 如果您针对旧版浏览器,转换后的输出 可能会让您大吃一惊 。
不要使用
for...in
@zipcodeman 建议使用
for...in
语句,但对于迭代数组,应避免使用
for-in
,该语句旨在
枚举
对象属性。
它不应该用于类似数组的对象,因为:
- 迭代的顺序无法保证;数组索引可能不是按数字顺序访问的。
- 继承的属性也会被枚举。
第二点是,它会给你带来很多问题,例如,如果你扩展
Array.prototype
对象以在其中包含一个方法,那么该属性也会被枚举。
例如:
Array.prototype.foo = "foo!";
var array = ['a', 'b', 'c'];
for (var i in array) {
console.log(array[i]);
}
上面的代码将在控制台中记录“a”、“b”、“c”和“foo!”。
如果你使用一些严重依赖本机原型增强的库(例如 MooTools ),那么这可能会成为一个问题。
正如我之前所说,
for-in
语句用于
枚举
对象属性,例如例如:
var obj = {
"a": 1,
"b": 2,
"c": 3
};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
// or if (Object.prototype.hasOwnProperty.call(obj,prop)) for safety...
console.log("prop: " + prop + " value: " + obj[prop])
}
}
在上面的例子中,
hasOwnProperty
方法允许您仅枚举
自己的属性
。就是这样,只有对象物理上具有的属性,没有继承的属性。
我建议您阅读以下文章:
是的,假设您的实现包括
for
...
of
在
ECMAScript 2015
(“Harmony”版本)中引入的功能……这在当今是一个相当安全的假设。
它的工作原理如下:
// REQUIRES ECMASCRIPT 2015+
var s, myStringArray = ["Hello", "World"];
for (s of myStringArray) {
// ... do something with s ...
}
或者更好的是,因为 ECMAScript 2015 还提供了块范围变量:
// REQUIRES ECMASCRIPT 2015+
const myStringArray = ["Hello", "World"];
for (const s of myStringArray) {
// ... do something with s ...
}
// s is no longer defined here
(变量
s
在每次迭代中都不同,但仍可以声明
const
位于循环体内,只要它没有在那里被修改。)
关于稀疏数组的说明:JavaScript 中的数组实际上可能没有存储其
length
所报告的那么多项;该数字只是比存储值的最高索引大一。如果数组包含的元素少于其长度所指示的元素,则称其为
稀疏
。例如,一个数组只在索引 3、12 和 247 处有项是完全合法的;这种数组的
length
是 248,尽管它实际上只存储了 3 个值。如果您尝试访问任何其他索引处的项目,该数组将似乎在那里具有
undefined
值,但该数组与实际存储
undefined
值的数组仍然不同。您可以通过多种方式看到这种差异,例如 Node REPL 显示数组的方式:
> a // array with only one item, at index 12
[ <12 empty items>, 1 ]
> a[0] // appears to have undefined at index 0
undefined
> a[0]=undefined // but if we put an actual undefined there
undefined
> a // it now looks like this
[ undefined, <11 empty items>, 1 ]
因此,当您想要“循环遍历”数组时,您需要回答一个问题:您是要循环遍历其长度指示的整个范围并处理
undefined
以查找任何缺失元素,还是只想处理实际存在的元素?这两种方法都有很多应用;这取决于您使用数组的用途。
如果您使用
for
..
of
迭代数组,则循环主体将执行
length
次,并且对于数组中实际不存在的任何项目,循环控制变量将设置为
undefined
。根据您“执行某项操作”代码的细节,该行为可能正是您想要的,但如果不是,则应使用其他方法。
当然,有些开发人员别无选择,只能使用其他方法,因为无论出于何种原因,他们都针对尚不支持
for
...
of
的 JavaScript 版本。
只要您的 JavaScript 实现符合 ECMAScript 规范的
上一
版本(例如,排除了 9 之前的 Internet Explorer 版本),那么您可以使用
Array#forEach
迭代器方法,而不是循环。在这种情况下,您传递一个函数,该函数将在数组中的每个项目上调用:
var myStringArray = [ "Hello", "World" ];
myStringArray.forEach( function(s) {
// ... do something with s ...
} );
如果您的实现支持 ES6+,您当然可以使用箭头函数:
myStringArray.forEach( s => {
// ... do something with s ...
} );
与
for
...
of
不同,
.forEach
仅对数组中实际存在的元素调用该函数。如果传递了包含三个元素且长度为 248 的假设数组,它只会调用该函数三次,而不是 248 次。如果您希望以这种方式处理稀疏数组,那么即使您的解释器支持
for
...
of
,
.forEach
也可能是最佳选择。
最后一个选项适用于 所有 版本的 JavaScript,即 显式计数循环 。您只需从 0 计数到比长度少 1,并使用计数器作为索引。基本循环如下所示:
var i, s, myStringArray = [ "Hello", "World" ], len = myStringArray.length;
for (i=0; i<len; ++i) {
s = myStringArray[i];
// ... do something with s ...
}
这种方法的一个优点是您可以选择如何处理稀疏数组。上述代码将运行循环主体完整的
length
次,对于任何缺失元素,将
s
设置为
undefined
,就像
for
..
of
一样;如果您只想处理稀疏数组中实际存在的元素,例如
.forEach
,您可以在索引上添加一个简单的
in
测试:
var i, s, myStringArray = [ "Hello", "World" ], len = myStringArray.length;
for (i=0; i<len; ++i) {
if (i in myStringArray) {
s = myStringArray[i];
// ... do something with s ...
}
}
根据您的实现的优化,将长度值分配给局部变量(而不是在循环条件中包含完整的
myStringArray.length
表达式)可以显著提高性能,因为它每次都会跳过属性查找。您可能会看到在循环初始化子句中完成的长度缓存,如下所示:
var i, len, myStringArray = [ "Hello", "World" ];
for (len = myStringArray.length, i=0; i<len; ++i) {
如果您需要,显式计数循环还意味着您可以访问每个值的索引。索引还会作为额外参数传递给您传递给
forEach
的函数,因此您也可以通过这种方式访问​​它:
myStringArray.forEach( (s,i) => {
// ... do something with s and i ...
});
for
...
of
不会为您提供与每个对象关联的索引,但只要您正在迭代的对象实际上是
Array
的实例(而不是
for
..
of
适用的其他可迭代类型之一),您可以使用
Array#entries
方法将其更改为 [index, item] 对的数组,然后对其进行迭代:
for (const [i, s] of myStringArray.entries()) {
// ... do something with s and i ...
}
其他人提到的
for
...
in
语法用于循环遍历对象的属性;由于 JavaScript 中的数组只是一个具有数字属性名称(以及自动更新的
length
属性)的对象,因此理论上你可以用它循环遍历数组。但问题是它并不局限于数字属性值(请记住,即使方法实际上也只是其值为闭包的属性),也不能保证按数字顺序迭代这些属性。因此,
for
...
in
语法不应
用于
循环遍历数组。
您可以使用
map
,这是一种功能编程技术“ rel =“ noreferrer”> python
和<a href="http://en.wikipedia.org/wiki/wiki/haskel/haskel_%28programming_language; p>
393465589
一般语法是:
218449697
in General
func
在阵列的项目。但是对于JavaScript,它可以采用第二个参数,即项目索引,第三个参数是数组本身。
array.map
的返回值
是另一个数组,因此您可以这样使用:
262183427
,现在x是
[10,20,20,30,40]
。 p>
您不必在线编写功能。它可能是一个单独的功能。
720254862
,其等效等效于:
287871791
除外t获取
new_list
。