在 Java 中,您可以使用for
循环来遍历数组中的对象,如下所示:
String[] myStringArray = {"Hello", "World"};
for (String s : myStringArray)
{
// Do something
}
你能用 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); }
详细示例如下。
for
循环:var myStringArray = ["Hello","World"];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
console.log(myStringArray[i]);
//Do something
}
优点
break
和continue
流量控制语句缺点
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方法。
反模式:
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);
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
;报告的数字仅比存储值的最高索引大 1。如果数组包含的元素少于其长度指示的元素,则称其为sparse。例如,拥有一个仅包含索引 3、12 和 247 项的数组是完全合法的;在length
这样的阵列的报告为248,尽管它仅实际存储3个值。如果您尝试访问任何其他索引处的项目,该数组将显示为那里的undefined
值。所以当你想“循环”一个数组时,你有一个问题要回答:你想循环遍历它的长度和过程指示的整个范围吗?undefined
s 对于任何缺失的元素,还是只想处理实际存在的元素?这两种方法都有很多应用;它只取决于您使用数组的目的。
如果您使用for
..迭代数组,of
循环体将执行length
多次,并且循环控制变量将设置undefined
为数组中实际不存在的任何项目。根据“使用”代码的详细信息,该行为可能是您想要的,但如果不是,您应该使用不同的方法。
当然,一些开发人员别无选择,只能使用不同的方法,因为无论出于何种原因,他们的目标都是不支持for
...的 JavaScript 版本of
。
只要您的 JavaScript 实现符合ECMAScript 规范的先前版本(例如排除 Internet Explorer 9 之前的版本),您就可以使用Array#forEach
迭代器方法而不是循环。在这种情况下,您传递一个要在数组中的每个项目上调用的函数:
var myStringArray = [ "Hello", "World" ];
myStringArray.forEach( function(s) {
// ... do something with s ...
} );
与for
...不同of
,.forEach
仅对数组中实际存在的元素调用该函数。如果通过我们的假设数组,它包含三个元素,长度为 248,它只会调用该函数 3 次,而不是 248 次。它还区分丢失的元素和实际设置为的元素undefined
;对于后者,它仍然会调用函数,undefined
作为参数传递。如果这是您想要处理稀疏数组.forEach
的方式,即使您的解释器支持for
...也可能是一种方法of
。
最后一个选项适用于所有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
在循环条件中包含完整表达式)可以显着提高性能,因为它每次都跳过属性查找;在我的机器上使用 Rhino,加速是 43%。
您可能会在循环初始化子句中看到长度缓存,如下所示:
var i, len, myStringArray = [ "Hello", "World" ];
for (len = myStringArray.length, i=0; i<len; ++i) {
如果需要,显式计数循环还意味着您可以访问每个值的索引。索引也作为额外参数传递给您传递给的函数forEach
,因此您也可以通过这种方式访问它:
myStringArray.forEach( function(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 中的 Array 只是一个具有数字属性名称(和一个自动更新的length
属性)的对象,理论上您可以使用它循环遍历一个 Array。但问题是它并不将自己限制为数字属性值(请记住,即使方法实际上也只是其值为闭包的属性),也不能保证按数字顺序迭代这些值。因此,for
...in
语法不应用于循环遍历数组。
您可以使用map
,这是一种函数式编程技术,在Python和Haskell等其他语言中也可用。
[1,2,3,4].map( function(item) {
alert(item);
})
一般语法是:
array.map(func)
通常func
会采用一个参数,它是数组的一项。但是在 JavaScript 的情况下,它可以采用作为项目索引的第二个参数和作为数组本身的第三个参数。
的返回值array.map
是另一个数组,因此您可以像这样使用它:
var x = [1,2,3,4].map( function(item) {return item * 10;});
现在 x 是[10,20,30,40]
。
您不必编写内联函数。它可以是一个单独的功能。
var item_processor = function(item) {
// Do something complicated to an item
}
new_list = my_list.map(item_processor);
这相当于:
for (item in my_list) {item_processor(item);}
除非你没有得到new_list
.
(直接回答你的问题:现在你可以了!)
大多数其他答案是正确的,但他们没有提到(在撰写本文时)ECMAScript 6 2015带来了一种新的迭代机制,即for..of
循环。
这种新语法是在 JavaScript 中迭代数组的最优雅方式(只要您不需要迭代索引)。
它目前适用于 Firefox 13+、Chrome 37+,并且它本身不适用于其他浏览器(请参阅下面的浏览器兼容性)。幸运的是,我们有 JavaScript 编译器(例如Babel),可以让我们今天使用下一代功能。
它也适用于 Node.js(我在 0.12.0 版上测试过)。
迭代一个数组
// You could also use "let" or "const" instead of "var" for block scope.
for (var letter of ["a", "b", "c"]) {
console.log(letter);
}
迭代对象数组
const band = [
{firstName : 'John', lastName: 'Lennon'},
{firstName : 'Paul', lastName: 'McCartney'}
];
for(const member of band){
console.log(member.firstName + ' ' + member.lastName);
}
迭代生成器:
(示例摘自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)
function* fibonacci() { // A generator function
let [prev, curr] = [1, 1];
while (true) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
for (const n of fibonacci()) {
console.log(n);
// Truncate the sequence at 1000
if (n >= 1000) {
break;
}
}
兼容性表: http : //kangax.github.io/es5-compat-table/es6/#For..of loops
规范: http : //wiki.ecmascript.org/doku.php?id= harmony: iterators
在 JavaScript 中,不建议使用 for-in 循环遍历数组,但最好使用如下for
循环:
for(var i=0, len=myArray.length; i < len; i++){}
它也进行了优化(“缓存”数组长度)。如果您想了解更多信息,请阅读我关于该主题的帖子。