在 JavaScript 中循环遍历数组

IT技术 javascript arrays loops for-loop
2020-12-09 00:39:16

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

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

你能用 JavaScript 做同样的事情吗?

6个回答

三个主要选项:

  1. for (var i = 0; i < xs.length; i++) { console.log(xs[i]); }
  2. xs.forEach((x, i) => console.log(x));
  3. 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
}

优点

  • 适用于各种环境
  • 您可以使用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));

除非您计划支持古老的平台(例如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);

3. ES6for-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方法允许您仅枚举自己的属性就是这样,只有对象物理上具有的属性,没有继承的属性。

我建议您阅读以下文章:

我知道这个答案早于异步和Promise,但我觉得在任何与现代 JavaScript 相关的对话中都值得一提:“forEach不等待Promise。确保您在使用Promise(或异步函数)作为forEach回调时了解其含义。 ” developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-03-05 00:39:16
es6 的缺点for-of:无法获取当前索引
2021-03-05 00:39:16

是的,假设您的实现包括for...ofECMAScript 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值。所以当你想“循环”一个数组时,你有一个问题要回答:你想循环遍历它的长度和过程指示的整个范围吗?undefineds 对于任何缺失的元素,还是只想处理实际存在的元素?这两种方法都有很多应用;它只取决于您使用数组的目的。

如果您使用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语法应用于循环遍历数组。

请注意,如果代码被调用的次数足够多并且检测到长度没有被循环修改,一些解释器(例如 V8)会自动缓存数组的长度。虽然缓存长度仍然不错,但当您的代码被调用足够多的次数以实际产生影响时,它可能无法提供速度提升。
2021-03-06 00:39:16

您可以使用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.

@hasen,该Array.prototype.map方法是 ECMAScript 第 5 版标准的一部分,尚未在所有实现中可用(例如 IE 缺少它),也用于迭代数组,我认为该Array.prototype.forEach方法在语义上更正确......也请不要不建议使用 for-in 语句,有关详细信息,请参阅我的答案:)
2021-02-10 00:39:16
使用Array.forEach. map用于生成新数组。
2021-02-26 00:39:16

for (myStringArray 的常量) {

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

大多数其他答案是正确的,但他们没有提到(在撰写本文时)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++){}

它也进行了优化(“缓存”数组长度)。如果您想了解更多信息,请阅读我关于该主题的帖子