在JavaScript中循环遍历数组

3986
在Java中,您可以使用for循环遍历数组中的对象,如下所示:
String[] myStringArray = {"Hello", "World"};
for (String s : myStringArray) {
    // Do something
}

我能在JavaScript中做同样的事情吗?


7
我有点困惑,使用增强for循环来访问对象是可以的吗?而用顺序循环来填充一个对象呢?这样做正确吗? - Mark Szymanski
54
不,这很简单,数组对象有数字索引,所以你想按照数字顺序迭代这些索引,一个连续的循环可以保证这一点,增强型的 for-in 循环枚举对象属性时没有特定的顺序,并且也枚举继承的属性...对于迭代数组,始终建议使用连续的循环。 - Christian C. Salvadó
7
相关 - https://dev59.com/mG435IYBdhLWcg3wjwzS - jondavidjohn
9
这里有一个循环遍历数组的解决方案的基准测试:http://jsben.ch/#/Q9oD5 - EscapeNetscape
13
@CMS 不,实际上并不简单。在其他所有语言中都很简单。但在JS中却非常复杂,因为你需要使用inof,它们有不同的用法。此外,你还需要使用forEach和丑陋而烦人的基于索引的循环方式。其他现代语言循环集合都很容易和直观,没有任何意外或困惑。JS也可以做到,但它并没有。 - jpmc26
显示剩余5条评论
46个回答

40

如果您想以简洁的方式编写快速循环,并且可以进行反向迭代:

for (var i=myArray.length;i--;){
  var item=myArray[i];
}

这样做的好处是缓存了长度(类似于 for (var i=0, len=myArray.length; i<len; ++i),而不像 for (var i=0; i<myArray.length; ++i)),同时打字更少。

有时候甚至需要反向迭代,例如在遍历动态NodeList时要删除DOM中的元素。


16
对于那些不理解为什么这么巧妙的人:首先计算i--表达式,只要它不是 falsish(假值),循环就会继续进行...然后计数器被递减。当i变成零时,循环将立即退出,因为在Javascript中零是一个 falsish 值。 - Stijn de Witt
5
"falsish? You mean falsey. Let's all stick to the proper terminology to avoid confusion ;)" “falsish?你的意思是falsey。让我们都坚持使用正确的术语以避免混淆;)” - danwellman

37

是的,你可以使用循环在JavaScript中完成相同的操作,但不仅限于此。在JavaScript中有多种方法可以遍历数组。想象一下你有以下数组,并且你想要对它进行循环:

var arr = [1, 2, 3, 4, 5];

这些是解决方案:

1)for 循环

for 循环是 JavaScript 中常用的遍历数组的方式,但对于大型数组来说它并不被认为是最快的解决方案:

for (var i=0, l=arr.length; i<l; i++) {
  console.log(arr[i]);
}

2) While循环

While循环被认为是遍历长数组的最快方法,但在JavaScript代码中通常使用较少:

let i=0;

while (arr.length>i) {
    console.log(arr[i]);
    i++;
}

3)do while
do while 做的事情与 while 相同,只是语法略有不同,如下所示:

let i=0;
do {
  console.log(arr[i]);
  i++;
}
while (arr.length>i);

这些是JavaScript循环的主要方式,但还有几种其他方式。

此外,我们使用for in循环来遍历JavaScript对象。

另请查看JavaScript数组上的map()filter()reduce()等函数。它们可能比使用whilefor更快、更好地完成任务。

如果您想了解有关JavaScript中数组异步函数的更多信息,请参阅此文章。

函数式编程最近在开发领域引起了很大的轰动。这是有充分理由的:函数式技术可以帮助您编写更具声明性的代码,一眼就能明白,易于重构和测试。

函数式编程的基石之一就是列表及其操作的特殊用法。这些东西正是它们听起来的样子:一组事物和你对它们所做的事情。但是,函数式思维方式将它们与您预期的略有不同。

本文将仔细研究我喜欢称之为“三驾马车”的列表操作:map、filter和reduce。掌握这三个函数是成为能够编写干净的函数式代码并打开函数式和反应式编程的强大技术之门的重要一步。

这也意味着,你永远不必再写for循环了。

阅读更多>> 这里:


31

有一种方法可以使你的循环中几乎没有隐式作用域,并且不需要额外的变量。

var i = 0,
     item;

// Note this is weak to sparse arrays or falsey values
for ( ; item = myStringArray[i++] ; ){
    item; // This is the string at the index.
}

或者,如果您真的想要获取id并使用经典的 for 循环:

var i = 0,
    len = myStringArray.length; // Cache the length

for ( ; i < len ; i++ ){
    myStringArray[i]; // Don't use this if you plan on changing the length of the array
}

现代浏览器都支持迭代器方法 forEach, map, reduce, filter 以及其他许多在 数组原型 上的方法。


3
请注意,一些解释器(例如V8)如果代码被反复调用并且检测到循环中的长度未被修改,则会自动缓存数组的长度。 - Phrogz
感谢@Phrogz提供的信息,虚拟机可以进行很多优化,但由于旧版浏览器没有这个功能,因此最好还是针对它进行优化,因为这样做非常便宜。 - Gabriel
1
@Gabriel:为什么?请给出现实世界的例子,说明不缓存长度实际上会成为性能瓶颈。我遵循“过早优化是万恶之源”的方法。一旦遇到真正造成问题的那个循环,我会修复它... - Stijn de Witt
1
@StijndeWitt 我认为这只是一个风格问题。老实说,我不再使用for循环,而是依赖于underscore来完成诸如_.each、_.map等任务。当我编写这样的循环时,主要是缓存长度,以便所有变量声明都在函数顶部的同一位置。在这方面遵循我的建议对任何真实世界的应用程序都没有影响。过早地进行优化非常糟糕,但如果优化结果来自风格决策,我认为它实际上并不重要。 - Gabriel
1
@Gabriel 我相信JavaScript已经支持数组的map函数,不需要引入额外的库。 - Noz
@Noz map 函数最近才得到了广泛的浏览器支持... 当然,Underscore 提供的不仅仅是这个。但我完全同意,如果可能且不会过于复杂,使用原生 JS 而非库始终是更好的选择。依赖关系是不好的。尽量避免它们。 - Stijn de Witt

29

数组循环:

for(var i = 0; i < things.length; i++){
    var thing = things[i];
    console.log(thing);
}

对象循环:

for(var prop in obj){
    var propValue = obj[prop];
    console.log(propValue);
}

29

我强烈建议使用Underscore.js库。它提供了各种函数,可用于迭代数组/集合。

例如:

_.each([1, 2, 3], function(num){ alert(num); });
=> alerts each number in turn...

7
对于这个问题的新发现者,我想指出 Lo-Dash,它是 Underscore 的精神继承者,在许多方面都有所改进。 - Mark Reed
3
如果ECMA-262已经添加了forEach方法,为什么要使用underscore?原生代码始终更好。 - Walter Chapilliquen - wZVanG

29

在JavaScript中,有多种遍历数组的方式。

通用的循环:

var i;
for (i = 0; i < substr.length; ++i) {
    // Do something with `substr[i]`
}

ES5的forEach方法:

substr.forEach(function(item) {
    // Do something with `item`
});

jQuery.each:

jQuery.each(substr, function(index, item) {
    // Do something with `item` (or `this` is also `item` if you like)
});

看看这里了解详细信息,或者您也可以查看MDN了解如何在JavaScript中循环数组,以及使用jQuery请参阅jQuery each


26

如果有人对数组迭代的多种机制的性能方面感兴趣,我已经准备好了以下JSPerf测试:

https://jsperf.com/fastest-array-iterator

Performance results

结果:

传统的for()迭代器是迄今为止最快的方法,特别是在与缓存的数组长度一起使用时。

let arr = [1,2,3,4,5];

for(let i=0, size=arr.length; i<size; i++){
    // Do something
}

Array.prototype.forEach()Array.prototype.map()方法是最慢的近似值,这可能是由于函数调用开销造成的。


最好使用i = i + 1而不是i++ - DarckBlezzer
2
可以改进:请使用++i而不是i++,这将避免创建临时对象。因此,它减少了内存使用和CPU时间(无需分配)! - PowerStat
@PowerStat,你能提供一个关于这个的链接或参考资料吗?我从未听说过它,听起来很有趣... - colxi
1
@colxi 对于这样有趣的事情,你应该阅读Herb Sutter和Scott Meyers的C++核心内容。++i与i++的问题来自书籍:《Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions》 - 我认为你也可以在http://www.gotw.ca/上找到它,但可以证明适用于每种编程语言。 - PowerStat

22

我还没有看到这种变化,个人认为这是最好的:

给定一个数组:

var someArray = ["some", "example", "array"];

你可以循环它而不必访问长度属性:
for (var i=0, item; item=someArray[i]; i++) {
  // item is "some", then "example", then "array"
  // i is the index of item in the array
  alert("someArray[" + i + "]: " + item);
}

看这个 JsFiddle,演示了如下内容:http://jsfiddle.net/prvzk/

这仅适用于稀疏数组。也就是说,在数组的每个索引处实际上都有一个值。然而,我发现在JavaScript中,我几乎从不使用稀疏数组... 在这种情况下,通常更容易使用对象作为映射/哈希表。如果您确实有一个稀疏数组,并且想要循环遍历0 .. length-1,则需要使用for (var i = 0; i < someArray.length; ++i)结构,但您仍然需要在循环内部使用if来检查当前索引处的元素是否实际定义。

此外,正如CMS在下面的评论中提到的那样,您只能在不包含任何虚假值的数组上使用此方法。例如,该示例中的字符串数组有效,但如果您有空字符串或数字为0或NaN等,则循环将会提前终止。再次强调,在实践中,这对我来说几乎从未是问题,但这是需要记住的一点,这使得这个循环在使用之前需要考虑... 这可能会淘汰一些人 :)

我喜欢这个循环的原因是:
  • 写起来很短
  • 不需要访问(更不用缓存)长度属性
  • 要访问的项会自动在循环体内以你选择的名称定义。
  • 非常自然地与array.push和array.splice结合使用,像列表/堆栈一样使用数组

之所以这样做有效,是因为数组规范要求当您从索引>=数组长度的位置读取项时,它将返回未定义。当您写入此类位置时,它实际上会更新长度。

对我来说,这个结构最接近我喜欢的Java 5语法:

for (String item : someArray) {
}

除了还知道循环内当前索引的好处外,也能做到这一点。


14
请注意,使用这种方法,当循环遇到 falsey 值 时会立即停止,例如空字符串、0falseNaNnullundefined,甚至在 i 达到长度之前就会停止循环。例如:http://jsfiddle.net/prvzk/1/ - Christian C. Salvadó
3
循环条件可以是(item=someArray[i]) !== undefined - daniel1426

21

有 4 种数组迭代的方式:

// 1: for

for (let i = 0; i < arr.length; ++i) {
  console.log(arr[i]);
}

// 2: forEach

arr.forEach((v, i) => console.log(v));

// 3: for in

for (let i in arr) {
  console.log(arr[i]);
}

// 4: for of

for (const v of arr) {
  console.log(v);
}

总结:1和3的解决方案会创建额外的变量,2会创建额外的函数上下文。最好的方法是第四种方法 - 使用“for of”。


2
你介意详细说明为什么“for of”是最好的,而不是其他的吗? - YesItsMe
1
它不会创建不必要的变量或函数上下文。但是,如果您不在意小缺点,可以使用任何一个对您来说更舒适的选项。@YesItsMe 感谢您的提问。 - Aleksandr Golovatyi

21

如果你正在使用jQuery库,考虑使用http://api.jquery.com/jQuery.each/

以下是文档内容:

jQuery.each(collection, callback(indexInArray, valueOfElement))

返回值:对象

描述:一个通用的迭代器函数,可用于无缝迭代对象和数组。具有长度属性的数组和类数组对象(例如函数的参数对象)通过数字索引从0到length - 1进行迭代。其他对象通过它们的命名属性进行迭代。

$.each()函数与$(selector).each()不同,后者仅用于迭代jQuery对象。$.each()函数可用于迭代任何集合,无论是映射(JavaScript对象)还是数组。在数组情况下,每次回调会传递一个数组索引和相应的数组值。(该值也可以通过this关键字访问,但Javascript始终将this值包装为Object,即使它是一个简单的字符串或数字值)。该方法返回其迭代的第一个参数,即对象。


6
同意但有例外。不要低估额外依赖的影响。除非代码已经严重依赖 jQuery,否则我建议不要这样做。 - Stijn de Witt
2
更新:现在,您可以使用Array.forEach来获得与本机数组相同的效果。 - Stijn de Witt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接