题 在JavaScript中循环遍历数组


在Java中,您可以使用 for 循环遍历数组中的对象,如下所示:

String[] myStringArray = {"Hello", "World"};
for (String s : myStringArray)
{
    // Do something
}

你能用JavaScript做同样的事吗?


2421
2018-06-10 00:04


起源


好的,所以我有点困惑,在访问对象时可以使用增强的for循环吗?并使用顺序填充一个?它是否正确? - Mark Szymanski
不,它非常简单,数组对象有数字索引,所以你想要 迭代 在数字顺序中的那些索引上,顺序循环确保了 增强  for-in 循环 枚举 对象属性,没有特定的顺序,它还枚举继承的属性... for 迭代 总是建议使用数组顺序循环... - CMS
相关的 - stackoverflow.com/questions/5349425/... - jondavidjohn
相关的 - stackoverflow.com/q/6208964/31671 - alex
jsben.ch/#/Q9oD5 <=这里是一组用于循环遍历数组的解决方案的基准 - EscapeNetscape


答案:


使用顺序 for 循环:

var myStringArray = ["Hello","World"];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
    alert(myStringArray[i]);
    //Do something
}

@zipcodeman建议使用 for...in 声明,但用于迭代数组 for-in 应该避免,该声明是为了 枚举 对象属性。

它不应该用于类似数组的对象,因为:

  • 不保证迭代的顺序,可能无法按数字顺序访问数组索引。
  • 还枚举了继承的属性。

第二点是,它可以给你很多问题,例如,如果你延长了 Array.prototype 要包含一个方法的对象,该属性也将被枚举。

例如:

Array.prototype.foo = "foo!";
var array = ['a', 'b', 'c'];

for (var i in array) {
  alert(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...
    alert("prop: " + prop + " value: " + obj[prop])
  }
}

在上面的例子中 hasOwnProperty 方法允许您仅枚举 自己的财产就是这样,只有对象物理上具有的属性,没有继承属性。

我建议你阅读以下文章:


3060
2018-06-10 00:07



为什么要投票? for...in应该 避免 对于类似数组的对象! - CMS
这就是原因(CMS他自己) stackoverflow.com/questions/1885317/... - OscarRyz
@DoubleGras,我认为这是一个不为大家所共享的意见。看到: stackoverflow.com/questions/5752906/... 要么 groups.google.com/forum/?fromgroups#!topic/jsmentors/... - Matthijs Wessels
@StijndeWitt不,因为如果你有任何打破就会打破 “falsey” 数组中的值: false, undefined, 0, "", NaN。 - Phrogz
jsperf.com/caching-array-length/4  这是一个测试,看看是否值得在Javascript循环中缓存数组的长度 - Enrico


是的,但仅当您的实施包括 for...of 功能介绍 ECMAScript 2015 (“和谐”版本)。

它的工作原理如下:

// REQUIRES ECMASCRIPT 2015+
var s, myStringArray = ["Hello", "World"];
for (s of myStringArray) {
  // ... do something with s ...
}

或者更好,因为ECMAScript 2015还提供了块范围的变量 let 和 const

// REQUIRES ECMASCRIPT 2015+
const myStringArray = ["Hello", "World"];
for (const s of myStringArray) {
  // ... do something with s ...
}
// s is no longer defined here

然而,许多JavaScript开发人员仍然在一个尚未出现的环境中工作 - 特别是如果编写代码以在Web浏览器中运行,那么网站开发人员通常无法确定他们的客户将使用哪种浏览器/版本。

如果你可以假设JavaScript解释器符合 以前 ECMAScript规范的版本(例如,排除9之前的Internet Explorer版本),然后你可以使用 forEach 迭代器方法而不是循环。在这种情况下,您传递一个要在数组中的每个项目上调用的函数:

var myStringArray = [ "Hello", "World" ];
myStringArray.forEach( function(s) { 
     // ... do something with s ...
} );

但是,即使这样做太多了,你也想要一些有用的东西 所有 JavaScript的版本,然后你必须使用显式计数循环。最安全的版本,正确处理稀疏数组,如下所示:

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) {

for...in 别人提到的语法是循环对象的属性;因为JavaScript中的数组只是一个具有数字属性名称的对象(并且是自动更新的 length你可以理论上用它循环一个数组。但问题是它并不局限于数值属性值(请记住,即使方法实际上只是其值为闭包的属性),也不会按数字顺序迭代它们。因此, for...in 语法应该  用于循环遍历数组。


871
2018-04-16 02:03



请注意,某些解释器(例如V8)将自动缓存数组的长度 如果 调用代码足够多次,它检测到循环不修改长度。虽然缓存长度仍然很好,但是当您的代码被调用足够多次以实际产生影响时,它可能无法提供速度提升。 - Phrogz
@ mark-reed你能解释一下你使用的原因吗? i in myStringArray 在你的例子?这怎么可能是假的? - Denis V
@DenisV:假。 a=[1,2,3,4]; delete a[2]; for (j in a) { console.log(j); }  输出0,1,3和4。 a.length 仍然是5。 - Mark Reed
我不是在暗示 for j in a。我正在证明这一点 in 检查不是多余的,正如你声称的那样,通过显示所有索引并显示0和之间存在一个 length-1 那不存在。我也可以刚刚打印出来 2 in a,确实如此 false尽管你说这是不可能的事实。 - Mark Reed
@GrijeshChauhan - 对。例如,IE到版本8不支持它。看到 这个问题。 - Mark Reed


您可以使用 map,这是一种功能性编程技术,也可用于其他语言 蟒蛇 和 哈斯克尔

[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


392
2018-06-10 00:09



不,但它可以更强大。看一下这个: joelonsoftware.com/items/2006/08/01.html - hasen
该特定示例可能更好地使用 Array.forEach。 map 用于生成新数组。 - harto
@hasen, Array.prototype.map 方法是ECMAScript第5版标准的一部分,尚未在所有实现中可用(例如IE缺少它),也适用于 迭代 在一个阵列我认为 Array.prototype.forEach 方法更多 语义 正确...也请不要建议for-in声明,请参阅我的答案以获取更多详细信息:) - CMS
和...之间的不同 forEach 和 map 是前者不返回迭代的结果。 map (有时是a.k.a. collect,但非常不同 apply)明确地用于将数组的每个元素转换为相应的结果;这是1比1 制图, 由此得名。它是整个运营系列的一部分,包括 reduce (从整个数组产生一个结果)和 filter (产生原始数组的子集),依此类推。而 forEach 只对每个元素做一些事情,语义未指定。 - Mark Reed
Downvote因为如果你实际上没有映射某些内容,那么使用[。.map]会产生误导。 [] .forEach具有语义意义,并​​且还将相同的三个参数传递给函数。 - gengkev


在JavaScript中,不建议使用for-in循环遍历数组,但最好使用for循环,例如:

for(var i=0, len=myArray.length; i < len; i++){}

它也被优化(“缓存”数组长度)。如果你想了解更多, 阅读我关于这个主题的帖子


102
2017-12-07 07:24



myArray.forEach(function(obj){});仍然是最好的 - Jan Sverre
一个微小的改进:你可以使用 ++i 代替 i++ - roberkules
++i 是一个古老的学校优化,现代编译器为你做了一个for循环很久以前:) stackoverflow.com/a/1547433/1033348 - ngryman
@Jannis .forEach 有几件事反对它。 1)不是原生2)需要一个新的执行上下文为每个索引,这是相当昂贵的,似乎有点矫枉过正(见 dmitrysoshnikov.com/ecmascript/chapter-1-execution-contexts) - Jose
你必须小心使用这个循环。我开始使用它并且因为我犯了一个错误而难以追踪错误。如果你嵌套两个循环,如下所示: jsfiddle.net/KQwmL/1。您必须小心在两个循环中以不同的方式命名var len,否则第二个循环将覆盖第一个len。 - Rui Marques


for(var s of myStringArray){

(直接回答你的问题:现在你可以!)

大多数其他答案是正确的,但他们没有提及(截至本文撰写时) ECMA脚本 6 2015年 正在带来一种新的迭代机制 for..of 循环。

这种新语法是在javascript中迭代数组的最优雅方式(只要你不需要迭代索引),但它还没有被浏览器广泛支持。

它目前适用于Firefox 13 +,Chrome 37+,它本身不能与其他浏览器一起使用(请参阅下面的浏览器兼容性)。幸运的是我们有JS编译器(例如 巴别塔)允许我们今天使用下一代功能。

它也适用于Node(我在版本0.12.0上测试过它)。

迭代一个数组

// You could also use "let" instead of "var" for block scope.
for (var letter of ["a", "b", "c"]) { 
   console.log(letter); 
}

迭代一个对象数组

var band = [
  {firstName : 'John', lastName: 'Lennon'}, 
  {firstName : 'Paul', lastName: 'McCartney'}
];

for(var 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 (let 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循环

规格:  http://wiki.ecmascript.org/doku.php?id=harmony:iterators

}


92
2017-08-11 15:54



如果您使用的是ES6,我会建议 const s 代替 var s - joeytwiddle


Opera,Safari,Firefox和Chrome现在都共享一组增强的Array方法,用于优化许多常见循环。

您可能不需要所有这些,但它们可能非常有用,或者如果每个浏览器都支持它们。

Mozilla Labs发布了他们和的算法 WebKit的 两者都使用,以便您可以自己添加它们。

过滤 返回满足某些条件或测试的项数组。

一切 如果每个数组成员都通过测试,则返回true。

一些 如果有任何通过测试,则返回true。

的forEach 在每个数组成员上运行一个函数,不返回任何内容。

地图 就像forEach一样,但它返回每个元素的操作结果数组。

这些方法都为它们的第一个参数提供了一个函数,并且有一个可选的第二个参数,该参数是一个对象,当它们循环遍历函数时,它们要在数组成员上施加范围。

忽略它直到你需要它。

指数 和 lastIndexOf 找到与其参数完全匹配的第一个或最后一个元素的适当位置。

(function(){
    var p, ap= Array.prototype, p2={
        filter: function(fun, scope){
            var L= this.length, A= [], i= 0, val;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        val= this[i];
                        if(fun.call(scope, val, i, this)){
                            A[A.length]= val;
                        }
                    }
                    ++i;
                }
            }
            return A;
        },
        every: function(fun, scope){
            var L= this.length, i= 0;
            if(typeof fun== 'function'){
                while(i<L){
                    if(i in this && !fun.call(scope, this[i], i, this))
                        return false;
                    ++i;
                }
                return true;
            }
            return null;
        },
        forEach: function(fun, scope){
            var L= this.length, i= 0;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        fun.call(scope, this[i], i, this);
                    }
                    ++i;
                }
            }
            return this;
        },
        indexOf: function(what, i){
            i= i || 0;
            var L= this.length;
            while(i< L){
                if(this[i]=== what)
                    return i;
                ++i;
            }
            return -1;
        },
        lastIndexOf: function(what, i){
            var L= this.length;
            i= i || L-1;
            if(isNaN(i) || i>= L)
                i= L-1;
            else
                if(i< 0) i += L;
            while(i> -1){
                if(this[i]=== what)
                    return i;
                --i;
            }
            return -1;
        },
        map: function(fun, scope){
            var L= this.length, A= Array(this.length), i= 0, val;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        A[i]= fun.call(scope, this[i], i, this);
                    }
                    ++i;
                }
                return A;
            }
        },
        some: function(fun, scope){
            var i= 0, L= this.length;
            if(typeof fun== 'function'){
                while(i<L){
                    if(i in this && fun.call(scope, this[i], i, this))
                        return true;
                    ++i;
                }
                return false;
            }
        }
    }
    for(p in p2){
        if(!ap[p])
            ap[p]= p2[p];
    }
    return true;
})();

81
2018-06-10 02:43



另外:从版本9开始,IE支持forEach,请参阅 forEach方法MSDN - rwitzel


使用while循环...

var i=0, item, items = ['one','two','three'];
while(item = items[i++]){
    console.log(item);
}

日志:'one','two','three'

而对于相反的顺序,一个更有效的循环

var items = ['one','two','three'], i = items.length;
while(i--){
    console.log(items[i]);
}

日志:'三','两','一'

或经典 for 循环

var items = ['one','two','three']
for(var i=0, l = items.length; i < l; i++){
    console.log(items[i]);
}

日志:'one','two','three'

参考: http://www.sitepoint.com/google-closure-how-not-to-write-javascript/


62
2018-01-05 09:15



如果任何数组元素是假的,那么“while”语法的第一个例子将不起作用。 - Chris Cooper
...而这个while循环等价于:for(var i = 0,item; item = items [i]; i ++),它不需要事先声明索引和项目变量...... - Stijn de Witt
@StijndeWitt但是这适用于: 如果值是 falsy,它不会起作用... - yckart
在迭代集合时,您应该关注优化。因为它可能影响页面性能。 - Zaheer Ahmed


介绍

自从我上大学以来,我用Java,JavaScript,Pascal编程, ABAP,PHP,Progress 4GL,C / C ++以及其他一些我现在想不到的语言。

虽然它们都有自己的语言特性,但每种语言都有许多相同的基本概念。这些概念包括程序/功能, IF-statements, FOR-loops,和 WHILE-loops。


传统的 for-循环

传统的 for 循环有三个组成部分:

  1. 初始化: 在第一次执行查看块之前执行
  2. 条件: 每次执行循环块之前检查一个条件,如果为false则退出循环
  3. 事后的想法: 每次执行循环块后执行

这三个组件通过a分开 ; 符号。这三个组件中的每一个的内容都是可选的,这意味着以下内容是最小的 for 循环可能:

for (;;) {
    // Do stuff
}

当然,你需要包括一个 if(condition === true) { break; }  或者 if(condition === true) { return; } 在里面的某个地方 for-loop让它停止运行。

但是,通常,初始化用于声明索引,条件用于将该索引与最小值或最大值进行比较,并使用事后补充来增加索引:

for (var i = 0, length = 10; i < length; i++) {
    console.log(i);
}

使用传统的 for 循环遍历数组

循环遍历数组的传统方法是:

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

或者,如果您更喜欢向后循环,则执行以下操作:

for (var i = myArray.length - 1; i > -1; i--) {
    console.log(myArray[i]);
}

但是,有许多可能的变化,例如这一个:

for (var key = 0, value = myArray[key], length = myArray.length; key < length; value = myArray[++key]) {
    console.log(value);
}

......或者这一个......

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

......或者这个:

var key = 0, value;
for (; value = myArray[key++];){
    console.log(value);
}

无论哪种效果最好,主要取决于个人品味和您正在实施的具体用例。

请注意,所有浏览器都支持这些变体,包括非常旧的浏览器!


一个 while 循环

一种替代方案 for 循环是一个 while循环。要遍历数组,您可以这样做:

var key = 0;
while(value = myArray[key++]){
    console.log(value);
}

像传统一样 for 循环, while 即使是最古老的浏览器也支持循环。

另请注意,每个while循环都可以重写为a for 循环。例如, while 上面的循环行为与此完全相同 for-循环:

for(var key = 0; value = myArray[key++];){
    console.log(value);
}

For...in 和 for...of

在JavaScript中,您也可以这样做:

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

但是,这应该谨慎使用,因为它的行为与传统行为不同 for 在所有情况下循环,并且存在需要考虑的潜在副作用。看到 为什么在数组迭代中使用“for ... in”是一个坏主意? 更多细节。

作为替代 for...in,现在也有 for...of。以下示例显示了a之间的区别 for...of 循环和 for...in 循环:

var myArray = [3, 5, 7];
myArray.foo = "hello";

for (var i in myArray) {
  console.log(i); // logs 0, 1, 2, "foo"
}

for (var i of myArray) {
  console.log(i); // logs 3, 5, 7
}

此外,您需要考虑没有Internet Explorer版本支持 for...of (边缘12+ 并且那 for...in 至少需要Internet Explorer 10。


Array.prototype.forEach()

替代 for-loops是 Array.prototype.forEach(),它使用以下语法:

myArray.forEach(function(value, key, myArray) {
    console.log(value);
});

Array.prototype.forEach() 所有现代浏览器以及Internet Explorer 9及更高版本都支持。


图书馆

最后,许多实用程序库也有自己的 foreach 变异。 AFAIK,最受欢迎的三种是:

jQuery.each(),在 jQuery的

$.each(myArray, function(key, value) {
    console.log(value);
});

_.each(),在 Underscore.js

_.each(myArray, function(value, key, myArray) {
    console.log(value);
});

_.forEach(),在 Lodash.js

_.forEach(myArray, function(value, key) {
    console.log(value);
});

48
2018-02-29 18:56





如果你想要一种简洁的方法来编写快速循环  你可以反向迭代:

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中删除项目的位置。


35
2018-06-04 16:26



对于那些没有得到如此巧妙的人:首先评估i--表达式并允许循环在它不是假的时候继续...然后计数器递减。一旦我变为零,它将突破循环,因为零是Javascript中的虚假值。 - Stijn de Witt
falsish?你的意思是假的。让我们都坚持使用适当的术语以避免混淆;) - danwellman
我已经看到了我认为是大师的人所使用的伪造一词。如果它对他们来说足够好,那对我来说就足够了。同样令人失望的是看到我的评论实际上是ontopic并添加了解释/洞察得到0赞成,但是我评论中对一个术语的评论获得的评论得到4.很好我只是优先考虑的问题。 - Stijn de Witt
“缓存长度”?长度在数组中以整数形式存储,每次访问时都不会测量。将length的值复制到另一个变量中没有任何好处。 - Mouscellaneous
@Mouscellaneous这些天肯定没有;在过去几年中迭代JavaScript数组缓存JavaScript端的长度(而不是跨越实现)是一个明显的性能增益(当微优化时)。例如, for (var i=0,len=array.length;i<len;++i) 写作是一个常见的,明智的循环。 - Phrogz


有一种方法可以在循环中具有非常小的隐式范围并且消除额外的变量。

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.
}

或者,如果你真的想要获得身份证并拥有非常经典的身份 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
}

现代浏览器都支持迭代器方法 forEachmapreducefilter 以及其他一些方法 数组原型


26
2018-05-16 22:52



请注意,如果代码被调用足够多次并且它检测到循环未修改长度,则某些解释器(例如V8)将自动缓存数组的长度。 - Phrogz
感谢@Phrogz的信息确实虚拟机可以进行很多优化,但是由于旧的浏览器没有这个优点,因此优化它仍然是最佳实践,因为它非常便宜。 - Gabriel
@Gabriel:为什么?请给出真实世界的例子,说明不缓存长度实际上是一个性能瓶颈。我遵循'过早优化是万恶之源'的方法。一旦我遇到它,我会修复一个实际上会出现问题的循环...... - Stijn de Witt
@StijndeWitt imo这只是一个风格问题。老实说,我甚至不再使用for循环而是依赖于下划线来执行这些事情,例如_.each,_. map等。当我写这样的循环时,我主要缓存长度,以便我的所有变量声明都在我的函数顶部的一个位置。根据我在这方面的建议对任何现实世界的应用来说都是无关紧要的。过早优化是非常糟糕的,但如果优化恰好来自风格决策,我认为它并不重要。 - Gabriel
@Gabriel我相信JavaScript已经支持数组上的map函数,不需要为此引入额外的lib。 - Noz