遍历JavaScript中的数组


3141

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

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

您可以在JavaScript中做同样的事情吗?


5
好的,所以我有点困惑,在访问对象时可以使用增强的for循环吗?并使用顺序填充一个?它是否正确?
Mark Szymanski 2010年

45
不,这真的很简单,数组对象具有数字索引,因此您想按数字顺序迭代这些索引,顺序循环可确保增强 for-in循环枚举对象属性而无特定顺序,还枚举继承的属性。 .. 始终建议对数组进行迭代顺序循环...
CMS 2010年


6
jsben.ch/#/Q9oD5 <=这里是一堆用于遍历数组的解决方案的基准
EscapeNetscape

3
@CMS不,这不是很简单。其他所有语言都非常简单。这是可笑的复杂的JS,你必须inof那个都可以使用,并做不同的事情。然后,您还将拥有forEach基于丑陋和令人讨厌的索引的循环。每一种其他现代语言都使循环浏览收藏集变得容易而直接,而不会感到惊讶或困惑。JS也可以,但事实并非如此。
jpmc26 '18 -10-5

Answers:


3954

您有几种选择:

1.顺序for循环:

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

优点

  • 适用于各种环境
  • 您可以使用breakcontinue流控制语句

缺点

  • 太冗长
  • 势在必行
  • 容易出现一对一错误(有时也称为栅栏错误

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

除非计划支持古老的平台(例如IE11),否则箭头功能也得到了广泛的实现。你也很安全。

优点

  • 非常简短。
  • 陈述式

缺点

  • 无法使用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);

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方法仅允许您枚举自己的属性,仅枚举对象物理上具有的属性,而没有枚举属性。

我建议您阅读以下文章:


20
这是什么原因(由CMS他自己) stackoverflow.com/questions/1885317/...
OscarRyz

14
@DoubleGras,我认为这不是所有人都认同的观点。请参阅:stackoverflow.com/questions/5752906/...groups.google.com/forum/?fromgroups#!topic/jsmentors/...
Matthijs尔斯

2
有人认为您需要缓存长度...请看我的回答,您甚至不需要一次访问它,更不用说缓存它了:for(var i = 0,item; item = myStringArray [i]; i ++){/ *在此处使用项目* /}
Stijn de Witt

13
@StijndeWitt没有,因为那休息,如果您有任何“falsey”你的数组中的值:falseundefined0""NaN
Phrogz

5
jsperf.com/caching-array-length/4 这是一个测试,看是否值得在Javascript循环中缓存数组的长度
Enrico

1113

是的,假设您的实现包括ECMAScript 2015(“ Harmony”版本)中引入的for...of功能... 如今,这是一个非常安全的假设。

它是这样的:

// 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; 该报告的数字仅比存储值的最高索引大一个。如果数组包含的元素少于其长度所指示的元素,则称其为sparse。例如,拥有仅在索引3、12和247处有项的数组是完全合法的;在length这样的阵列的报告为248,尽管它仅实际存储3个值。如果您尝试访问任何其他索引处的项目,则该数组似乎在其中具有undefined值。因此,当您要“循环遍历”数组时,您需要回答一个问题:是否要遍历数组的长度和过程所指示的整个范围undefined表示缺少的元素,还是只想处理实际存在的元素?两种方法都有很多应用。这仅取决于您使用数组的目的。

如果使用for.. 遍历一个数组of,则循环的主体将执行length一次,并且将循环控制变量设置undefined为数组中实际不存在的任何项目。根据您的“执行某项操作”代码的详细信息,该行为可能是您想要的,但如果不是,则应使用其他方法。

当然,有些开发人员别无选择,只能使用其他方法,因为无论出于何种原因,他们都针对的是尚不支持for... 的JavaScript版本of

只要你的JavaScript实现兼容与以前的 ECMAScript规范的版本(这就排除了,例如,前9 Internet Explorer的版本),那么你可以使用Array#forEach迭代方法,而不是一个循环。在这种情况下,您传递一个要在数组中每个项目上调用的函数:

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

for... 不同的是of.forEach仅调用数组中实际存在的元素的函数。如果传递带有三个元素且长度为248的假设数组,则它将仅调用该函数三次,而不是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不会为您提供与每个对象关联的索引,但是只要您要迭代的对象实际上是一个Arrayfor.. 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语法应用于遍历数组。


21
请注意,如果调用了足够多次的代码,并且某些代码会检测到循环未修改该长度,则某些解释器(例如V8)将自动缓存该数组的长度。尽管缓存的长度仍然不错,但是当您的代码被调用足够多次而实际上不起作用时,它可能不会提高速度。
Phrogz 2012年

2
@ mark-reed您能否解释一下为什么i in myStringArray在示例中使用了它?这怎么可能是假的?
丹尼斯五世

2
@DenisV:错误。a=[1,2,3,4]; delete a[2]; for (j in a) { console.log(j); } 输出0,1,3,和4 a.length仍然是5
马克里德

1
我不建议for j in a。我in通过显示所有索引并显示0到0之间的索引来证明检查不是多余的,正如您所声称的length-1那样。尽管您说那是不可能的,但我本来也可以印刷2 in a,的确false是这样。
马克·里德

2
@GrijeshChauhan-正确。例如,通过版本8的IE不支持它。看到这个问题
马克·里德2014年

442

您可以使用map,这是一种功能编程技术,在其他语言(例如PythonHaskell)中也可以使用。

[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


7
不,但是它可以更强大。检查一下:joelonsoftware.com/items/2006/08/01.html
hasen

97
使用可以更好地实现该特定示例Array.forEachmap用于生成新数组。
harto 2010年

21
@hasen,该Array.prototype.map方法是ECMAScript 5th Edition标准的一部分,尚未在所有实现上都可用(例如IE缺少该方法),也用于迭代数组,我认为该Array.prototype.forEach方法在语义上更正确...也请不要提出建议,请参阅我的回答以获取更多详细信息:)
CMS 2010年

3
forEach和之间的区别在于,map前者不返回迭代结果。 map(有时也称为collect,但与完全不同apply)明确用于将数组的每个元素转换为相应的结果;这是一对一的映射关系,因此得名。它是整个操作系列的一部分,包括reduce(从整个数组产生单个结果)和filter(产生原始数组的一个子集)等等。虽然forEach只是做一些事情。每一个元素,语义不明。
Mark Reed 2014年

4
拒绝投票,因为如果您实际上并未映射某些内容,则使用[] .map会产生误导。[] .forEach具有语义意义,并​​且还将相同的三个参数传递给该函数。
gengkev '16

120

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

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

它还进行了优化(“缓存”阵列长度)。如果您想了解更多信息,请阅读我关于该主题的文章


2
myArray.forEach(function(obj){}); 仍然是最好的
Jan Sverre'1

一个小小的改进:您可以使用++i代替i++
roberkules 2012年

14
++i这是一种古老的优化方法,很久以前现代编译器就在for循环中为您完成:) stackoverflow.com/a/1547433/1033348
ngryman 2012年

6
您必须小心使用此循环。我开始使用它,并且由于我犯了一个错误而很难跟踪错误。如果您嵌套两个这样的循环:jsfiddle.net/KQwmL/1。您必须小心在两个循环中为var len命名,否则第二个循环将覆盖第一个len。
Rui Marques 2012年

1
瑞马克斯-你能说出你的变量i_stopi_end代替len。它具有可读性(如果不是,那么!),并且您自然会避免这种问题(因为您的其他循环将得到j_stop)。
Chip Hogg

118

for(让我们的myStringArray){

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

多数其他答案都是正确的,但截至本文撰写之时,它们并未提及ECMA Script  6  2015带来了一种进行迭代的新机制,即for..of循环。

这种新的语法是在javascript中迭代数组的最优雅的方法(只要您不需要迭代索引)。

目前,它可与Firefox 13 +,Chrome 37+一起使用,并且本身无法与其他浏览器一起使用(请参阅下面的浏览器兼容性)。幸运的是,我们拥有JS编译器(例如Babel),它们使我们今天可以使用下一代功能。

它也可以在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

}


如果您使用的是ES6,我建议const s不要使用var s
joeytwiddle

在我对大型数组的测试中,var s of arr与使用简单的计数器for循环和在nodejs中按索引检索元素相比,使用执行时间几乎是其两倍(1.9倍)
theferrit32 '19

为什么在第一行和最后一行中出现这些奇怪的内容?
彼得·莫滕森

91

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

您可能不需要全部,但它们可能非常有用,或者每个浏览器都支持它们。

Mozilla实验室发布了它们和WebKit都使用的算法,因此您可以自己添加它们。

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

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

如果通过测试,则某些返回true。

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

map类似于forEach,但是它返回每个元素的操作结果数组。

这些方法的第一个参数都带有一个函数,第二个参数是一个可选参数,第二个参数是一个对象,您希望在它们遍历函数时将其作用域强加给数组成员。

忽略它,直到您需要它为止。

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

(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;
})();

1
另外:自版本9起,IE支持forEach,请参见forEach方法MSDN
rwitzel

74

介绍

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

尽管它们都有自己的语言特质,但每种语言都具有许多相同的基本概念。这样的概念包括过程/函数,- IF语句,- FOR循环和- WHILE循环。


传统for循环

传统for循环包含三个组成部分:

  1. 初始化:在第一次执行look块之前执行
  2. 条件:每次在执行循环块之前都要检查条件,如果为false则退出循环
  3. 事后思考:循环块执行后每次执行

这三个组件之间用;符号隔开。这三个组件中每个组件的内容都是可选的,这意味着以下内容是for可能的最小循环:

for (;;) {
    // Do stuff
}

当然,您需要在该-loop 内添加if(condition === true) { break; }if(condition === true) { return; }for以使其停止运行。

通常,尽管如此,初始化用于声明索引,条件用于将索引与最小值或最大值进行比较,事后才用于增加索引:

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循环都可以重写为for循环。例如,上述while循环的行为与此for-loop 完全相同:

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

For...infor...of

在JavaScript中,您还可以执行以下操作:

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

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

作为替代for...in,现在也有for...of。以下示例显示了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...ofEdge 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);
});

68

使用while循环...

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

日志:“一个”,“两个”,“三个”

而对于相反的顺序,循环效率更高

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]);
}

日志:“一个”,“两个”,“三个”

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


21
如果任何数组元素都是虚假的,则“ while”语法的第一个示例将不起作用。
克里斯·库珀

2
...而此while循环等效于:for(var i = 0,item; item = items [i]; i ++),这样就无需事先声明索引和项目变量了……
Stijn de Witt


39

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

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--表达式求值,并在不虚假的情况下使循环继续进行...之后,计数器递减。一旦我变为零,它将终止循环,因为零是Javascript中的虚假值。
Stijn de Witt

5
怪癖?你是假的 让我们都坚持正确的术语以避免混淆;)
danwellman

4
我见过我认为大师的人使用虚假术语。如果对他们足够好,对我也足够。同样令我失望的是,我的评论实际上是在主题上并添加了解释/洞察力,但获得0票赞成,但评论中对某个术语之以鼻的评论却得到了4分。嗯,我想这只是优先事项。
Stijn de Witt 2014年

“缓存长度”?长度以整数形式存储在数组中,并非每次访问时都会对其进行度量。将length的值复制到另一个变量中没有任何好处。
2013年

1
@Mouscellaneous这些天肯定没有;在过去的几年中,迭代JavaScript数组将JavaScript的长度缓存在缓存中(而不是遍及整个实现)是明显的性能提升(微优化时)。例如,这for (var i=0,len=array.length;i<len;++i)是一个常见的明智的编写循环。
Phrogz '16

36

在JavaScript 中以函数式编程方式遍历数组的一些用例:

1.遍历数组

const myArray = [{x:100}, {x:200}, {x:300}];

myArray.forEach((element, index, array) => {
    console.log(element.x); // 100, 200, 300
    console.log(index); // 0, 1, 2
    console.log(array); // same myArray object 3 times
});

注意:严格来说,Array.prototype.forEach()并不是一种函数方式,因为它作为输入参数使用的函数不应返回值,因此不能将其视为纯函数。

2.检查数组中的任何元素是否通过测试

const people = [
    {name: 'John', age: 23}, 
    {name: 'Andrew', age: 3}, 
    {name: 'Peter', age: 8}, 
    {name: 'Hanna', age: 14}, 
    {name: 'Adam', age: 37}];

const anyAdult = people.some(person => person.age >= 18);
console.log(anyAdult); // true

3.转换为新数组

const myArray = [{x:100}, {x:200}, {x:300}];

const newArray= myArray.map(element => element.x);
console.log(newArray); // [100, 200, 300]

注意:map()方法创建一个新数组,其结果是在调用数组中的每个元素上调用提供的函数。

4.总结一个特定的属性,并计算其平均值

const myArray = [{x:100}, {x:200}, {x:300}];

const sum = myArray.map(element => element.x).reduce((a, b) => a + b, 0);
console.log(sum); // 600 = 0 + 100 + 200 + 300

const average = sum / myArray.length;
console.log(average); // 200

5.基于原始数组创建一个新数组,但不对其进行修改

const myArray = [{x:100}, {x:200}, {x:300}];

const newArray= myArray.map(element => {
    return {
        ...element,
        x: element.x * 2
    };
});

console.log(myArray); // [100, 200, 300]
console.log(newArray); // [200, 400, 600]

6.计算每个类别的数量

const people = [
    {name: 'John', group: 'A'}, 
    {name: 'Andrew', group: 'C'}, 
    {name: 'Peter', group: 'A'}, 
    {name: 'James', group: 'B'}, 
    {name: 'Hanna', group: 'A'}, 
    {name: 'Adam', group: 'B'}];

const groupInfo = people.reduce((groups, person) => {
    const {A = 0, B = 0, C = 0} = groups;
    if (person.group === 'A') {
        return {...groups, A: A + 1};
    } else if (person.group === 'B') {
        return {...groups, B: B + 1};
    } else {
        return {...groups, C: C + 1};
    }
}, {});

console.log(groupInfo); // {A: 3, C: 1, B: 2}

7.根据特定条件检索数组的子集

const myArray = [{x:100}, {x:200}, {x:300}];

const newArray = myArray.filter(element => element.x > 250);
console.log(newArray); // [{x:300}] 

注意:filter()方法创建一个新数组,其中所有元素都通过了由提供的函数实现的测试。

8.对数组排序

const people = [
  { name: "John", age: 21 },
  { name: "Peter", age: 31 },
  { name: "Andrew", age: 29 },
  { name: "Thomas", age: 25 }
];

let sortByAge = people.sort(function (p1, p2) {
  return p1.age - p2.age;
});

console.log(sortByAge);

在此处输入图片说明

9.查找数组中的元素

const people = [ {name: "john", age:23},
                {name: "john", age:43},
                {name: "jim", age:101},
                {name: "bob", age:67} ];

const john = people.find(person => person.name === 'john');
console.log(john);

在此处输入图片说明

Array.prototype.find()方法返回满足提供的测试功能的数组中第一个元素的值。

参考文献


30

有一种方法可以在循环中几乎没有隐式作用域且不需要额外的变量的情况下进行操作。

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
}

现代浏览器都支持迭代方法forEachmapreducefilter和对其他方法主机数组原型


3
请注意,如果调用了足够多次的代码,并且某些代码会检测到循环未修改长度,则某些解释器(例如V8)将自动缓存该数组的长度。
Phrogz 2012年

感谢您提供@Phrogz信息,的确,VM可以进行很多优化,但是由于较旧的浏览器没有此优化,因此优化它仍然是最佳实践,因为它是如此便宜。
加布里埃尔

1
@Gabriel:为什么?请提供真实的示例,说明不缓存长度实际上是性能瓶颈。我遵循“过早的优化是万恶之源”的方法。我将修复一个实际上遇到问题的循环……
Stijn de Witt

1
@StijndeWitt imo这只是一个样式问题。老实说,我什至不再使用循环,而是依靠下划线来完成_.each,_。map等操作。当我这样写循环时,我主要是缓存长度,以便所有变量声明都放在函数顶部的一个位置。在这方面遵循我的建议对任何实际应用都是无关紧要的。过早的优化是非常糟糕的,但是如果优化恰巧是由风格决定引起的,我认为这实际上并不重要。
加百利

1
@Gabriel我相信JavaScript已经支持数组上的map函数,无需为此引入其他库。
Noz

28

在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


27

我会彻底建议您使用underscore.js库。它为您提供了各种功能,可用于遍历数组/集合。

例如:

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

7
对于这个问题的新发现者,我只想指出一下Lo-Dash,这是Underscore的精神继任者,可以在许多方面加以改进。
马克·里德

3
underscore如果已添加ECMA-262,为什么要使用forEach。本机代码总是更好。
Walter Chapilliquen-wZVANG

27

数组循环:

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);
}

27

是的,您可以使用循环在JavaScript中执行相同的操作,但不仅限于此,有很多方法可以对JavaScript中的数组进行循环。假设您在下面有这个数组,并且想对其进行循环:

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

这些是解决方案:

1)循环

一个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)在
A do while做相同的事情的同时while,做如下语法差异:

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

这些是执行JavaScript循环的主要方法,但是还有其他几种方法可以做到这一点。

另外,我们使用for in循环来循环JavaScript中的对象。

也看一下map()filter()reduce()等功能于JavaScript的数组。他们可能比使用whileand 更快,更好地完成工作for

如果您想了解有关JavaScript中数组上的异步函数的更多信息,那么这是一篇好文章。

如今,函数式编程在开发界引起了轰动。并有充分的理由:功能技术可以帮助您编写更具说明性的代码,使它们一目了然,重构和测试就更容易理解。

函数式编程的基础之一是它对列表和列表操作的特殊使用。这些东西听起来确实像是:事物的数组,以及您对它们所做的事情。但是实用的心态对待它们的方式与您预期的有所不同。

本文将仔细研究我称之为“三大”列表操作的内容:映射,过滤和缩小。围绕这三个功能,这是朝着编写干净的功能代码迈出的重要一步,并且为功能强大的功能性和响应式编程技术打开了大门。

这也意味着您无需再编写for循环。

在这里阅读更多>> :


在遍历数组时,在for循环和while循环之前是否确实存在性能差异?我印象中的差异主要是句法上的
Shea She's

24

如果有人对可用于Array迭代的多种机制的性能方面感兴趣,请准备以下JSPerf测试:

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

绩效结果

结果:

for()到目前为止,传统的迭代器是最快的方法,尤其是在与数组长度cached一起使用时

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 vs i ++出自这本书:杰出的C ++:47个工程难题,编程问题和解决方案-我还可以在gotw.ca上找到它,但可以针对每种编程语言进行证明。
PowerStat

21

如果您使用的是jQuery库,请考虑使用 http://api.jquery.com/jQuery.each/

从文档中:

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

返回: 对象

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

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


9
jQuery的一切吗?
例外

6
同意例外。不要低估额外依赖的影响。除了在已经大量使用jQuery的代码中,我对此提出建议。
Stijn de Witt

2
更新:这些天,您可以使用Array.forEach来获得与本机数组相同的效果。
Stijn de Witt

20

我还没有看到这种变化,我个人最喜欢这种变化:

给定一个数组:

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

您可以循环访问它,而无需访问length属性:

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等,则循环将过早中断。再次在实践中,这对我来说几乎不是问题,但是要牢记这一点,这使它成为循环使用之前要考虑的问题……对于某些人来说,这可能会使它失去资格:)

我喜欢这个循环的是:

  • 写的短
  • 无需访问(更不用说缓存)length属性
  • 要访问的项目会在循环体内自动以您选择的名称进行定义。
  • 非常自然地与array.push和array.splice结合使用数组,例如列表/堆栈

起作用的原因是数组规范要求当您从索引> =数组长度读取项目时,它将返回未定义。当您写入此类位置时,它实际上会更新长度。

对我而言,此构造最接近地模仿了我喜欢的Java 5语法:

for (String item : someArray) {
}

...还有一个额外的好处,就是还知道循环内的当前索引


13
请注意,这种方法的循环,就立即停止它找到一个falsey值,如空字符串,0falseNaNnull或者undefined,甚至在i达到最大长度,如:jsfiddle.net/prvzk/1
CMS

3
循环条件可能为(item=someArray[i]) !== undefined
daniel1426 2014年

18

有一种方法可以仅对自己的对象属性进行迭代,而不包括原型的属性:

for (var i in array) if (array.hasOwnProperty(i)) {
    // Do something with array[i]
}

但是它仍然会遍历自定义属性。

在JavaScript中,可以将任何自定义属性分配给任何对象,包括数组。

如果想遍历数组疏林,for (var i = 0; i < array.length; i++) if (i in array)array.forEaches5shim要使用。


以及如何使用for (var i in array) if (++i)
Daniel Sokolowski14年

15

有两种方法可以在JavaScript中进行操作。前两个示例是JavaScript示例。第三个使用JavaScript库,即jQuery使用该.each()函数。

var myStringArray = ["hello", "World"];
for(var i in myStringArray) {
  alert(myStringArray[i]);
}

var myStringArray = ["hello", "World"];
for (var i=0; i < myStringArray.length; i++) {
  alert(myStringArray[i]);
}

var myStringArray = ["hello", "World"];
$.each(myStringArray, function(index, value){
  alert(value);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>


for...in应该避免使用类似数组的对象
brk

15

最优雅,最快捷的方式

var arr = [1, 2, 3, 1023, 1024];
for (var value; value = arr.pop();) {
    value + 1
}

http://jsperf.com/native-loop-performance/8


编辑(因为我错了)


比较用于遍历100000个项目的数组的方法,并每次使用新值进行最少的操作。

制备:

<script src="//code.jquery.com/jquery-2.1.0.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script>
    Benchmark.prototype.setup = function() {
        // Fake function with minimal action on the value
        var tmp = 0;
        var process = function(value) {
            tmp = value; // Hold a reference to the variable (prevent engine optimisation?)
        };

        // Declare the test Array
        var arr = [];
        for (var i = 0; i < 100000; i++)
            arr[i] = i;
    };
</script>

测试:

<a href="http://jsperf.com/native-loop-performance/16" 
   title="http://jsperf.com/native-loop-performance/16"
><img src="http://i.imgur.com/YTrO68E.png" title="Hosted by imgur.com" /></a>

此循环似乎不遵循数组中项目的顺序。
Deniz Ozger 2014年

我的测试是错误的。正确,现在显示所有LOOPS。jsperf.com/native-loop-performance/16
molokoloco 2014年

@bergi是正确的。该循环在遍历整个数组时会清除掉该数组。在大多数情况下,这不是您想要的。
Stijn de Witt 2014年

4
破坏假物品。
njzk2 2014年

12

优化的方法是缓存数组的长度,并使用单个var模式通过单个var关键字初始化所有变量。

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

如果迭代的顺序与您应该尝试反向循环无关紧要,则它是最快的,因为它减少了开销条件测试,并且减量在一条语句中:

var i,myStringArray = ["item1","item2"];
for (i =  myStringArray.length; i--) {
    alert(myStringArray[i]);
}

或更佳,更清洁的while循环使用:

var myStringArray = ["item1","item2"],i = myStringArray.length;
while(i--) {
   // do something with fruits[i]
}

12

在JavaScript中,有很多解决方案来循环数组。

下面的代码是流行的

/** Declare inputs */
const items = ['Hello', 'World']

/** Solution 1. Simple for */
console.log('solution 1. simple for')

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

console.log()
console.log()

/** Solution 2. Simple while */
console.log('solution 2. simple while')

let i = 0
while (i < items.length) {
  console.log(items[i++])
}

console.log()
console.log()

/** Solution 3. forEach*/
console.log('solution 3. forEach')

items.forEach(item => {
  console.log(item)
})

console.log()
console.log()

/** Solution 4. for-of*/
console.log('solution 4. for-of')

for (const item of items) {
  console.log(item)
}

console.log()
console.log()


12

如果要使用jQuery,它在其文档中有一个很好的示例:

 $.each([ 52, 97 ], function( index, value ) {
      alert( index + ": " + value );
 });

12

我认为最好的方法是使用Array.forEach函数。如果您不能使用它,我建议您从MDN获取polyfill。要使其可用,这肯定是在JavaScript中遍历数组的最安全方法。

Array.prototype.forEach()

因此,正如其他人所建议的,这几乎总是您想要的:

var numbers = [1,11,22,33,44,55,66,77,88,99,111];
var sum = 0;
numbers.forEach(function(n){
  sum += n;
});

这样可以确保在处理数组范围内需要的所有内容都在该范围内,并且只在处理数组的值,而不在处理对象属性和其他成员,这就是 for ..在做的。

使用常规的C风格 for在大多数情况下,循环即可。重要的是要记住,循环中的所有内容都与程序的其余部分共享其作用域,{}不会创建新的作用域。

因此:

var sum = 0;
var numbers = [1,11,22,33,44,55,66,77,88,99,111];

for(var i = 0; i<numbers.length; ++i){
  sum += numbers[i];
}

alert(i);

将输出“ 11”-可能不是您想要的。

一个有效的jsFiddle示例:https ://jsfiddle.net/workingClassHacker/pxpv2dh5/7/


10

它不是100%相同,但相似:

   var myStringArray = ['Hello', 'World']; // array uses [] not {}
    for (var i in myStringArray) {
        console.log(i + ' -> ' + myStringArray[i]); // i is the index/key, not the item
    }


1
看起来这将遇到与数组对象使用中的其他问题类似的问题,因为原型成员变量也将被for in捕获。
Kzqai 2012年

9

例如,我在Firefox控制台中使用过:

[].forEach.call(document.getElementsByTagName('pre'), function(e){ 
   console.log(e);
})

9
var x = [4, 5, 6];
for (i = 0, j = x[i]; i < x.length; j = x[++i]) {
    console.log(i,j);
}

干净很多...


与相比,这不是很干净z.forEach(j => console.log(j));
Sapphire_Brick

9

简短的回答:是的。您可以这样做:

var myArray = ["element1", "element2", "element3", "element4"];

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

在浏览器控制台中,您可以看到打印的内容,例如“ element1”,“ element2”等。


9

您可以使用Array.prototype.forEach(...)

var arr = ["apple", "banana", "cherry", "mango"];
arr.forEach((item, index)=>{
   //Some code...
});

Array.prototype.map(...)

var arr = ["apple", "banana", "cherry", "mango"];
arr.map((item, index)=>{
   //Some code...
});

或前面提到的jquery或for循环方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.