it-swarm.cn

For-each在JavaScript中的数组?

如何使用JavaScript循环遍历数组中的所有条目?

我以为它是这样的:

forEach(instance in theArray)

theArray是我的数组,但这似乎是不正确的。

4194
Dante1986

TL; DR

  • 不要使用for-in,除非您将其与保护措施一起使用或者至少知道它为什么会咬你。
  • 通常你最好的赌注

    • 一个for-of循环(仅限ES2015 +),
    • Array#forEachspecNAME_ | MDNNAME_ )(或其亲属somename__等)(仅限ES5 +),
    • 一个简单的老式forname__循环,
    • 或带有安全措施的for-in

但是有 很多 更多要探索,继续阅读......


JavaScript具有强大的语义,可以循环遍历数组和类似数组的对象。我把答案分成两部分:正版数组的选项,以及只是数组-like的选项,例如argumentsname__对象,其他可迭代对象(ES2015 +),DOM集合等等上。

我会很快注意到你可以使用ES2015选项现在,甚至在ES5引擎上,运算 ES2015到ES5。搜索“ES2015 transpiling”/“ES6 transpiling”了解更多...

好的,让我们来看看我们的选择:

对于实际阵列

你有三个选项 ECMAScript 5 (“ES5”),目前最广泛支持的版本,还有两个选项 ECMAScript 2015 (“ES2015”,“ES6”):

  1. 使用forEachname__和相关(ES5 +)
  2. 使用简单的forname__循环
  3. 使用for-in正确
  4. 使用for-of(隐式使用迭代器)(ES2015 +)
  5. 明确使用迭代器(ES2015 +)

细节:

1.使用forEachname__和相关

在任何模糊的现代环境(因此,不是IE8)中,您可以访问由ES5添加的Arrayname__功能(直接或使用polyfills),您可以使用forEachname __( specNAME_ | MDNNAME_ ):

var a = ["a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

forEachname__接受一个回调函数,并且可选地,一个值在调用该回调时用作thisname__(上面未使用)。为数组中的每个条目调用回调,按顺序跳过稀疏数组中不存在的条目。虽然我上面只使用了一个参数,但回调是用三个调用的:每个条目的值,该条目的索引,以及对你要迭代的数组的引用(如果你的函数还没有方便的话) )。

除非您支持IE8等过时的浏览器(截至2016年9月,NetApps的市场份额仅超过4%),您可以愉快地在没有垫片的通用网页中使用forEachname__。如果您确实需要支持过时的浏览器,则可以轻松完成填充/填充forEachname__(为几个选项搜索“es5 shim”)。

forEachname__的好处是您不必在包含范围内声明索引和值变量,因为它们作为参数提供给迭代函数,因此很好地限定了该迭代。

如果您担心为每个数组条目调用函数的运行时成本,请不要这样做; 细节

另外,forEachname__是“循环通过它们”的所有功能,但ES5定义了其他几个有用的“按照数组工作并做事”的功能,包括:

  • everyNAME_ (第一次回调返回falsename__或falsey时停止循环)
  • someNAME_ (第一次回调返回truename__或者truthy时停止循环)
  • filterNAME_ (创建一个新数组,其中包含filter函数返回truename__的元素,并省略返回falsename__的元素)
  • mapNAME_ (根据回调返回的值创建一个新数组)
  • reduceNAME_ (通过重复调用回调来构建一个值,传入以前的值;查看详细信息的规范;对于汇总数组的内容和许多其他内容很有用)
  • reduceRightNAME_ (与reducename__类似,但以降序而不是升序工作)

2.使用简单的forname__循环

有时旧的方式是最好的:

var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
    console.log(a[index]);
}

如果数组的长度在循环期间不会改变,而且它在性能敏感的代码中(不太可能),那么前面抓取长度的稍微复杂的版本可能是微小更快一点:

var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
    console.log(a[index]);
}

和/或向后计数:

var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
    console.log(a[index]);
}

但是使用现代JavaScript引擎,你很少需要榨取最后一滴果汁。

在ES2015及更高版本中,您可以将forname__循环的索引和值变量设置为本地:

let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
    let value = a[index];
}
//console.log(index); // Would cause "ReferenceError: index is not defined"
//console.log(value); // Would cause "ReferenceError: value is not defined"

当你这样做时,不仅valuename__而且indexname__被重新创建用于每个循环迭代,这意味着在循环体中创建的闭包保持对为该特定迭代创建的indexname __(和valuename__)的引用:

let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
    divs[index].addEventListener('click', e => {
        alert("Index is: " + index);
    });
}

如果您有五个div,如果单击第一个,则“索引为:0”,如果单击最后一个,则“索引为:4”。如果使用varname__而不是letname__,这会 not work。

3.使用for-in正确

你会让人们告诉你使用for-in,但是 这不是for-in的用途for-in遍历对象的可枚举属性,而不是数组的索引。 订单无法保证 ,即使在ES2015(ES6)中也没有。 ES2015确实定义了一个对象属性的顺序(通过 [[OwnPropertyKeys]][[Enumerate]] ,以及使用它们的东西,如 Object.getOwnPropertyKeys ),但它定义for-in将遵循那个命令。 (详情 其他答案 。)

仍然,can是有用的,特别是对于 稀疏数组 ,如果你使用适当的安全措施:

// `a` is a sparse array
var key;
var a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (key in a) {
    if (a.hasOwnProperty(key)  &&        // These are explained
        /^0$|^[1-9]\d*$/.test(key) &&    // and then hidden
        key <= 4294967294                // away below
        ) {
        console.log(a[key]);
    }
}

请注意两个检查:

  1. 该对象具有该名称的own属性(不是它从原型继承的那个),以及

  2. 密钥是普通字符串形式的基数为10的数字字符串,其值为<= 2 ^ 32 - 2(即4,294,967,294)。这个数字来自哪里?它是数组索引定义的一部分 在规范中 。其他数字(非整数,负数,大于2 ^ 32 - 2的数字)不是数组索引。它是2 ^ 32 - 2 的原因是使得最大索引值低于2 ^ 32 - 1 ,这是数组的lengthname__可以具有的最大值。 (例如,数组的长度适合32位无符号整数。)(Props to RobG指出评论 在我的博客文章中 我以前的测试不太正确。)

对于大多数数组而言,这是每次循环迭代的一小部分额外开销,但是如果你有一个稀疏数组,它可以是一种更有效的循环方式,因为它只循环实际存在的条目。例如,对于上面的数组,我们总共循环三次(对于键"0""10""10000" - 记住,它们是字符串),而不是10,001次。

现在,您不会每次都写这个,所以您可以将它放在您的工具箱中:

function arrayHasOwnIndex(array, prop) {
    return array.hasOwnProperty(prop) && /^0$|^[1-9]\d*$/.test(prop) && prop <= 4294967294; // 2^32 - 2
}

然后我们就像这样使用它:

for (key in a) {
    if (arrayHasOwnIndex(a, key)) {
        console.log(a[key]);
    }
}

或者,如果你只对“大多数情况下足够好”的测试感兴趣,你可以使用它,但是当它接近时,它并不完全正确:

for (key in a) {
    // "Good enough" for most cases
    if (String(parseInt(key, 10)) === key && a.hasOwnProperty(key)) {
        console.log(a[key]);
    }
}

4.使用for-of(隐式使用迭代器)(ES2015 +)

ES2015将iterators添加到JavaScript中。使用迭代器的最简单方法是使用新的for-of语句。它看起来像这样:

var val;
var a = ["a", "b", "c"];
for (val of a) {
    console.log(val);
}

输出:

一
 B 
 C 

在封面下,从数组中获取iterator并循环遍历它,从中获取值。这没有使用for-in所具有的问题,因为它使用由对象(数组)定义的迭代器,并且数组定义它们的迭代器遍历它们的条目(而不是它们的属性)。与ES5中的for-in不同,访问条目的顺序是其索引的数字顺序。

5.明确使用迭代器(ES2015 +)

有时,您可能希望使用迭代器显式。你也可以这样做,虽然它比for-of更笨拙。它看起来像这样:

var a = ["a", "b", "c"];
var it = a.values();
var entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

迭代器是与规范中的Iterator定义匹配的对象。每次调用它时,nextname__方法都会返回一个新的结果对象。结果对象有一个属性donename__,告诉我们是否已完成,以及一个属性valuename__,其中包含该迭代的值。 (donename__是可选的,如果它是falsename__,valuename__是可选的,如果它是undefinedname__。)

valuename__的含义因迭代器而异;数组支持(至少)三个返回迭代器的函数:

  • values():这是我上面使用的那个。它返回一个迭代器,其中每个valuename__是该迭代的数组条目(前面示例中的"a""b""c")。
  • keys():返回一个迭代器,其中每个valuename__是该迭代的键(因此对于我们的aname__,那将是"0",然后是"1",然后是"2")。
  • entries():返回一个迭代器,其中每个valuename__都是[key, value]形式的数组,用于该迭代。

对于类似数组的对象

除了真正的数组之外,还有array-like对象具有lengthname__属性和具有数字名称的属性:NodeListname__实例,argumentsname__对象等。我们如何遍历其内容?

使用上面的任何数组选项

上面的阵列方法中的至少一些,可能是大多数甚至全部,经常同样适用于类似数组的对象:

  1. 使用forEachname__和相关(ES5 +)

    Array.prototype上的各种函数都是“故意通用的”,通常可以通过 Function#callFunction#apply 用于类似数组的对象。 (请参阅本答案末尾的主持人提供的对象的警告,但这是一个罕见的问题。)

    假设您想在forEachname __的Nodename__属性上使用childNodesname__。你这样做:

    Array.prototype.forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
    

    如果你要做很多事情,你可能想把一个函数引用的副本放到一个变量中以便重用,例如:

    // (This is all presumably in some scoping function)
    var forEach = Array.prototype.forEach;
    
    // Then later...
    forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
    
  2. 使用简单的forname__循环

    显然,一个简单的forname__循环适用于类似数组的对象。

  3. 使用for-in正确

    具有与数组相同的安全措施的for-in也应该与类似数组的对象一起使用;上面#1上主机提供的对象的警告可能适用。

  4. 使用for-of(隐式使用迭代器)(ES2015 +)

    for-of将使用对象提供的迭代器(如果有的话);我们必须看看它如何与各种类似数组的对象一起使用,特别是主机提供的对象。例如,更新了NodeListname__中querySelectorAllname__的规范以支持迭代。 HTMLCollectionname__中getElementsByTagNamename__的规范不是。

  5. 显式使用迭代器(ES2015 +)

    看到#4,我们必须看看迭代器是如何发挥作用的。

创建一个真正的数组

其他时候,您可能希望将类似数组的对象转换为真正的数组。这样做非常简单:

  1. 使用 sliceNAME_ 数组方法

    我们可以使用数组的slicename__方法,就像上面提到的其他方法一样,“故意通用”,因此可以与类似数组的对象一起使用,如下所示:

    var trueArray = Array.prototype.slice.call(arrayLikeObject);
    

    因此,例如,如果我们想将NodeListname__转换为真正的数组,我们可以这样做:

    var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
    

    请参阅下面的警告主机提供的对象。特别要注意的是,这将在IE8及更早版本中失败,因为它不允许您像使用主机提供的对象一样使用thisname__。

  2. 使用 扩展语法(...

    也可以使用ES2015 扩展语法 使用支持此功能的JavaScript引擎:

    var trueArray = [...iterableObject];
    

    因此,例如,如果我们想将NodeListname__转换为真正的数组,使用扩展语法,这将变得非常简洁:

    var divs = [...document.querySelectorAll("div")];
    
  3. 使用Array.from(spec) | (MDN)

    Array.from(ES2015 +,但很容易polyfilled)从类似数组的对象创建一个数组,可选择首先通过映射函数传递条目。所以:

    var divs = Array.from(document.querySelectorAll("div"));
    

    或者,如果您想获得具有给定类的元素的标记名称数组,您将使用映射函数:

    // Arrow function (ES2015):
    var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
    
    // Standard function (since `Array.from` can be shimmed):
    var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
        return element.tagName;
    });
    

警告主持人提供的对象

如果将Array.prototype函数与Host-provided类似数组的对象(DOM列表和浏览器提供的其他内容而不是JavaScript引擎)一起使用,则需要确保在目标环境中进行测试以确保主机提供的对象行为正常。 大多数行为正确 (现在),但测试很重要。原因是您可能想要使用的大多数Array.prototype方法都依赖于Host提供的对象,它给出了抽象的实际答案 [[HasProperty]] 操作。在撰写本文时,浏览器对此做了很好的工作,但5.1规范确实允许主机提供的对象可能不诚实。它在 §8.6.2 ,在该部分开头附近的大表下方的几个段落),其中说:

除非另有说明,否则Host对象可以以任何方式实现这些内部方法;例如,一种可能性是特定Host对象的[[Get]][[Put]]确实获取并存储属性值,但[[HasProperty]]总是生成 false

(我无法在ES2015规范中找到相同的措辞,但它仍然是这样的。)同样,在撰写现代浏览器中的常见主机提供的类似数组的对象[例如NodeListname__实例] do handle [[HasProperty]]正确,但测试很重要。)

6345
T.J. Crowder

编辑 :这个答案毫无希望地过时了。对于更现代的方法,请查看 数组上可用的方法 。感兴趣的方法可能是:

  • 的forEach
  • 地图
  • 过滤
  • 压缩
  • 降低
  • 一切
  • 一些

JavaScript中迭代数组的标准方法 是一个Vanilla for循环:

var length = arr.length,
    element = null;
for (var i = 0; i < length; i++) {
  element = arr[i];
  // Do something with element
}

但请注意,如果您有一个密集数组,并且每个索引都被一个元素占用,这种方法才有用。如果数组是稀疏的,那么使用这种方法可能会遇到性能问题,因为您将遍历数组中不存在的许多索引。在这种情况下,for .. in循环可能是一个更好的主意。 但是 ,必须使用适当的安全措施来确保只对数组的所需属性(即数组元素)起作用,因为for..in-循环也将在旧版浏览器中枚举,或者如果其他属性定义为enumerable

ECMAScript 5中 在数组原型上会有一个forEach方法,但在旧版浏览器中不支持它。因此,为了能够始终如一地使用它,您必须具有支持它的环境(例如, Node.js 用于服务器端JavaScript),或使用“Polyfill”。然而,Polyfill对于这个功能来说是微不足道的,因为它使代码更容易阅读,所以它是一个很好的polyfill。

473
PatrikAkerstrand

如果你使用的是 jQuery library,你可以使用 jQuery.each

$.each(yourArray, function(index, value) {
  // do your stuff here
});

编辑:

根据问题,用户想要javascript中的代码而不是jquery,所以编辑是

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

向后循环

我认为reverse for循环值得一提:

for (var i = array.length; i--; ) {
     // process array[i]
}

好处:

  • 您不需要声明临时len变量,也不需要在每次迭代时与array.length进行比较,其中任何一个都可能是一分钟优化。
  • 以相反的顺序从DOM中删除兄弟姐妹 通常是 更有效 。 (浏览器需要减少内部数组中元素的移动。)
  • 如果你 修改数组 while循环,在index i之后或之后(例如你在array[i]中删除或插入一个项目),那么前向循环会跳过向左移动的项目position i,或重新处理向右移动的i项。在传统的for循环中,你can update i指向下一个需要处理的项 - 1,但简单地反转迭代的方向通常是 simpler 更优雅的解决方案
  • 类似地,当修改或删除 嵌套 DOM元素时,反向处理可以 规避错误 。例如,在处理子节点之前,请考虑修改父节点的innerHTML。到达子节点时,它将与DOM分离,在写入父内部HTML时,已被新创建的子节点替换。
  • 它是 更短 键入,和,比其他一些可用选项。虽然它失去了forEach()和ES6的for ... of

缺点:

  • 它以相反的顺序处理项目。如果你是从结果中构建一个新数组,或者在屏幕上打印东西,那么当然 输出将相反 相对于原始顺序。
  • 为了保持他们的顺序,重复地将兄弟姐妹插入DOM作为第一个孩子是 效率较低 。 (浏览器将不得不改变方向。)要按顺序有效地创建DOM节点,只需循环前进并正常追加(并使用“文档片段”)。
  • 反向循环是 混淆 初级开发人员。 (您可能会认为这是一个优势,具体取决于您的Outlook。)

我应该经常使用它吗?

一些开发人员使用reverse for loop 默认情况下,除非有充分的理由向前循环。

虽然性能提升通常微不足道,但它有点尖叫:

“只需对列表中的每个项目执行此操作,我不关心订单!”

然而在实践中, not 实际上是意图的可靠指示,因为它与你do关心订单的情况难以区分,并且确实need以反向循环。因此实际上需要另一个构造来准确表达“不关心”的意图,这在大多数语言中都是不可用的,包括ECMAScript,但可以调用,例如,forEachUnordered()

如果顺序无关紧要,并且 效率 是一个问题(在游戏或动画引擎的最里面的循环中),那么使用反向循环作为你的首选模式是可以接受的。只记得在现有代码中看到反向循环 并不一定意味着 该顺序无关紧要!

最好使用forEach()

一般来说,对于更高级别的代码,其中 清晰度和安全性 是更大的问题,我建议使用 Array::forEach 作为您的默认模式:

  • 很清楚阅读。
  • 它表明i不会在块中移位(这总是可能会隐藏在长forwhile循环中。)
  • 它为您提供了一个免费的闭包范围。
  • 它减少了局部变量的泄漏以及外部变量的意外碰撞(和突变)。

然后,当你在代码中看到反向循环时,这是一个暗示它被推翻的原因很充分(可能是上述原因之一)。并且看到传统的前向循环可能表明可能发生变换。

(如果对意图的讨论对你没有意义,那么你和你的代码可能会受益于观看Crockford的讲座 编程风格和你的大脑 。)


它是如何工作的?

for (var i = 0; i < array.length; i++) { ... }   // Forwards

for (var i = array.length; i--; )    { ... }   // Reverse

您会注意到i--是中间子句(我们通常会看到比较),最后一个子句是空的(我们通常会看到i++)。这意味着i--也用作条件用于延续。至关重要的是,每次迭代都会执行并检查之前

  • 如何在没有爆炸的情况下从array.length开始?

    因为i--在每次迭代时运行before,所以在第一次迭代中我们将实际访问array.length - 1处的项目,这避免了任何问题。 数组出界外 undefined项目。

  • 为什么它不会在索引0之前停止迭代?

    当条件i--计算为假值(当它产生0时),循环将停止迭代。

    诀窍在于,与--i不同,尾随的i--运算符会递减i但会产生值before减量。你的控制台可以证明:

    > var i = 5; [i, i--, i];

    [5, 5, 4]

    所以在最后一次迭代中,i先前是 1 i--表达式将其更改为 0 但实际上得到 1 (真实),条件通过了。在下一次迭代中i--i更改为 -1 但产生 0 (falsey),导致执行立即从循环底部退出。

    在传统的for循环中,i++++i是可以互换的(正如Douglas Crockford指出的那样)。但是在反向for循环中,因为我们的减量也是我们的条件表达式,如果我们想在索引0处理项目,我们必须坚持使用i--


琐事

有些人喜欢在反向for循环中绘制一个小箭头,并以眨眼结束:

for (var i = array.length; i --> 0 ;) {

积分转到WYL,向我展示反向循环的好处和恐怖。

94
joeytwiddle

一些 _ c _ - 样式语言使用foreach循环遍历枚举。在JavaScript中,这是通过 for..in循环结构完成的

var index,
    value;
for (index in obj) {
    value = obj[index];
}

有一个问题。 for..in将循环遍历每个对象的可枚举成员以及其原型上的成员。要避免读取通过对象原型继承的值,只需检查属性是否属于该对象:

for (i in obj) {
    if (obj.hasOwnProperty(i)) {
        //do stuff
    }
}

另外, ECMAScript 5 已向Array.prototype添加了一个 forEach 方法,可用于使用calback枚举数组(polyfill在文档中,因此您仍然可以将它用于旧版浏览器):

arr.forEach(function (val, index, theArray) {
    //do stuff
});

重要的是要注意Array.prototype.forEach在回调返回false时不会中断。 jQueryUnderscore.jseach上提供自己的变体,以提供可以短路的循环。

75
zzzzBov

如果要循环遍历数组,请使用标准的三部分for循环。

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

您可以通过缓存myArray.length或向后迭代来获得一些性能优化。

34
Quentin

我知道这是一个老帖子,已经有很多很棒的答案了。为了更完整一点,我想我会使用 AngularJS 扔另一个。当然,这只适用于你使用Angular的情况,显然,无论如何我仍然愿意。

angular.forEach接受2个参数和一个可选的第三个参数。第一个参数是迭代的对象(数组),第二个参数是迭代器函数,可选的第三个参数是对象上下文(在循环内部基本上称为'this'。

有不同的方法来使用forEach循环的角度。最简单也可能最常用的是

var temp = [1, 2, 3];
angular.forEach(temp, function(item) {
    //item will be each element in the array
    //do something
});

另一种将项目从一个数组复制到另一个数组的方法是

var temp = [1, 2, 3];
var temp2 = [];
angular.forEach(temp, function(item) {
    this.Push(item); //"this" refers to the array passed into the optional third parameter so, in this case, temp2.
}, temp2);

虽然,您不必这样做,但您可以简单地执行以下操作,它与上一个示例相同:

angular.forEach(temp, function(item) {
    temp2.Push(item);
});

现在有使用angular.forEach函数的优点和缺点,而不是内置的Vanilla风格的for循环。

优点

  • 易读性
  • 易写性
  • 如果可用,angular.forEach将使用ES5 forEach循环。现在,我将在cons部分中获得效率,因为forEach循环比for循环慢。我作为专业人士提到这一点,因为保持一致和标准化是件好事。

考虑以下2个嵌套循环,这些循环完全相同。假设我们有2个对象数组,每个对象包含一个结果数组,每个结果都有一个Value属性,它是一个字符串(或其他)。并且假设我们需要迭代每个结果,如果它们相等则执行一些操作: 

angular.forEach(obj1.results, function(result1) {
    angular.forEach(obj2.results, function(result2) {
        if (result1.Value === result2.Value) {
            //do something
        }
    });
});

//exact same with a for loop
for (var i = 0; i < obj1.results.length; i++) {
    for (var j = 0; j < obj2.results.length; j++) {
        if (obj1.results[i].Value === obj2.results[j].Value) {
            //do something
        }
    }
}

虽然这是一个非常简单的假设示例,但是我已经使用第二种方法编写了三次嵌入for循环,并且非常难以阅读,并为此写作。

缺点

  • 效率。 angular.forEach和本机forEach,就这一点来说,太多比正常的for循环慢......大约 慢90% 。因此对于大型数据集,最好坚持本机for循环。
  • 没有休息,继续或返回支持。 continue实际上由“ accident ”支持,在angular.forEach中继续你简单地将return;语句放在函数中,如angular.forEach(array, function(item) { if (someConditionIsTrue) return; });,这将导致它继续执行该迭代的函数。这也是由于本机forEach不支持中断或继续。

我相信还有其他各种优点和缺点,请随意添加任何你认为合适的东西。我觉得,如果你需要效率的话,最重要的是,坚持使用原生的for循环来满足你的循环需求。但是,如果你的数据集较小并且可以放弃一些效率来换取可读性和可写性,那么无论如何都要在那个坏男孩身上抛出angular.forEach

28
user2359695

如果你不介意清空数组:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

x将包含y的最后一个值,它将从数组中删除。你也可以使用shift()来提供和删除y中的第一个项目。

27
gaby de wilde

A forEach implementation( 参见jsFiddle ):

function forEach(list,callback) {
  var length = list.length;
  for (var n = 0; n < length; n++) {
    callback.call(list[n]);
  }
}

var myArray = ['hello','world'];

forEach(
  myArray,
  function(){
    alert(this); // do something
  }
);
27
nmoliveira

现在一个简单的解决方案是使用 underscore.js库 。它提供了许多有用的工具,例如each,并且会自动将作业委托给本机forEach(如果可用)。

CodePen示例 它的工作原理是:

var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});

也可以看看

24
Micka

jQuery 中有三个foreach实现,如下所示。

var a = [3,2];

$(a).each(function(){console.log(this.valueOf())}); //Method 1
$.each(a, function(){console.log(this.valueOf())}); //Method 2
$.each($(a), function(){console.log(this.valueOf())}); //Method 3
24
Rajesh Paul

可能for(i = 0; i < array.length; i++)循环不是最佳选择。为什么?如果你有这个:

var array = new Array();
array[1] = "Hello";
array[7] = "World";
array[11] = "!";

该方法将从array[0]调用array[2]。首先,这将首先引用你甚至没有的变量,第二个你不会在数组中有变量,第三个会使代码更大胆。看这里,这是我使用的:

for(var i in array){
    var el = array[i];
    //If you want 'i' to be INT just put parseInt(i)
    //Do something with el
}

如果你想让它成为一个函数,你可以这样做:

function foreach(array, call){
    for(var i in array){
        call(array[i]);
    }
}

如果你想打破一点逻辑:

function foreach(array, call){
    for(var i in array){
        if(call(array[i]) == false){
            break;
        }
    }
}

例:

foreach(array, function(el){
    if(el != "!"){
        console.log(el);
    } else {
        console.log(el+"!!");
    }
});

它返回:

//Hello
//World
//!!!
22
Federico Piragua

自ES6起:

list = [0, 1, 2, 3]
for (let obj of list) {
    console.log(obj)
}

其中of避免了与in相关的奇怪之处,并使其像任何其他语言的for循环一样工作,而let在循环中绑定i而不是在函数内。

当只有一个命令时(例如在上面的例子中),可以省略大括号({})。

21
Zaz

这是NON稀疏列表的迭代器,其中索引从0开始,这是处理document.getElementsByTagName或document.querySelectorAll时的典型场景。

function each( fn, data ) {

    if(typeof fn == 'string')
        eval('fn = function(data, i){' + fn + '}');

    for(var i=0, L=this.length; i < L; i++) 
        fn.call( this[i], data, i );   

    return this;
}

Array.prototype.each = each;  

用法示例:

示例#1

var arr = [];
[1, 2, 3].each( function(a){ a.Push( this * this}, arr);
arr = [1, 4, 9]

示例#2

each.call(document.getElementsByTagName('p'), "this.className = data;",'blue');

每个p标签都有class="blue"

示例#3

each.call(document.getElementsByTagName('p'), 
    "if( i % 2 == 0) this.className = data;",
    'red'
);

每个其他p标签都有class="red">

示例#4

each.call(document.querySelectorAll('p.blue'), 
    function(newClass, i) {
        if( i < 20 )
            this.className = newClass;
    }, 'green'
);

最后,前20个蓝色p标签变为绿色

使用字符串作为函数时的注意事项:该函数是在上下文中创建的,只有在您确定变量作用域时才应该使用。否则,最好传递范围更直观的函数。

17
Tim

本机 JavaScript 中没有任何for each循环。你可以使用库来获得这个功能(我推荐 Underscore.js ),在循环中使用一个简单的for

for (var instance in objects) {
   ...
}

但是请注意,可能有理由使用更简单的for循环(参见Stack Overflow问题为什么在数组迭代中使用“for ... in”这样一个坏主意?

var instance;
for (var i=0; i < objects.length; i++) {
    var instance = objects[i];
    ...
}
17
joidegn

几种方式 在JavaScript中循环一个数组,如下所示:

for - 这是最常见的一个。用于循环的完整代码块

var languages = ["Java", "JavaScript", "C#", "Python"];
var i, len, text;
for (i = 0, len = languages.length, text = ""; i < len; i++) {
    text += languages[i] + "<br>";
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

while - 在条件通过时循环。它似乎是最快的循环

var text = "";
var i = 0;
while (i < 10) {
    text +=  i + ") something<br>";
    i++;
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

do/while - 在条件为真时循环遍历代码块,将至少运行一次

var text = ""
var i = 0;
do {
    text += i + ") something <br>";
    i++;
}
while (i < 10);
document.getElementById("example").innerHTML = text;
<p id="example"></p>

功能循环 - forEachmapfilter,也reduce(它们遍历该函数,但如果您需要对数组执行某些操作,则使用它们等。

// For example, in this case we loop through the number and double them up using the map function
var numbers = [65, 44, 12, 4];
document.getElementById("example").innerHTML = numbers.map(function(num){return num * 2});
<p id="example"></p>

有关数组上函数编程的更多信息和示例,请查看博客文章JavaScript中的函数编程:map,filter和reduce

16
Alireza

使用Arrays的ECMAScript5(Javascript版本)。

forEach - 遍历数组中的每个项目,并为每个项目做任何你需要的事情。

['C', 'D', 'E'].forEach(function(element, index) {
  console.log(element + " is the #" + (index+1) + " in musical scale");
});

// Output
// C is the #1 in musical scale
// D is the #2 in musical scale
// E is the #3 in musical scale

万一,对使用一些内置功能的阵列操作更感兴趣。

map - 它使用回调函数的结果创建一个新数组。当您需要格式化数组元素时,可以使用此方法。

// Let's upper case the items in the array
['bob', 'joe', 'jen'].map(function(elem) {
  return elem.toUpperCase();
});

// Output: ['BOB', 'JOE', 'JEN']

reduce - 正如名称所说,它通过调用传入currenct元素的给定函数和前一次执行的结果将数组减少为单个值。

[1,2,3,4].reduce(function(previous, current) {
  return previous + current;
});
// Output: 10
// 1st iteration: previous=1, current=2 => result=3
// 2nd iteration: previous=3, current=3 => result=6
// 3rd iteration: previous=6, current=4 => result=10

every - 如果数组中的所有元素都在回调函数中通过测试,则返回true或false。

// Check if everybody has 18 years old of more.
var ages = [30, 43, 18, 5];  
ages.every(function(elem) {  
  return elem >= 18;
});

// Output: false

filter - 非常类似于除过滤器之外的每个过滤器返回一个数组,其中的元素返回true给定的函数。

// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
  return (elem % 2 == 0)
});

// Output: [2,4,6]

希望这会有用。

14
Anil Kumar Arya

没有内置的能力来打破forEach。要中断执行,请使用Array#some,如下所示:

[1,2,3].some(function(number) {
    return number === 1;
});

这是有效的,因为只要以数组顺序执行的任何回调返回true,some就会返回true,从而使其余的执行短路。 原始答案 参见数组原型 some

14
Priyanshu Chauhan

我还想将此作为一个反向循环的组合添加到上面的答案中,对于那些想要这种语法的人来说也是如此。

var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
    console.log(item);
}

优点:

这样做的好处是:你已经在第一个中有了引用,就像以后不需要用另一行声明一样。循环通过对象数组时很方便。

缺点:

只要引用为false,这将会中断 - falsey(未定义等)。它可以作为一个优势使用。但是,它会让它更难阅读。并且还取决于浏览器,它可以“不”优化,以比原始工作更快。

11
Volkan Seçkin Akbayır

使用$.map的jQuery方式:

var data = [1, 2, 3, 4, 5, 6, 7];

var newData = $.map(data, function(element) {
    if (element % 2 == 0) {
        return element;
    }
});

// newData = [2, 4, 6];
10
DanFromGermany

最接近你想法的方法是使用Array.forEach(),它接受将为数组的每个元素执行的clojure函数。

myArray.forEach(
  (item) => {
    // do something 
    console.log(item);
  }
);

另一种可行的方法是使用Array.map(),它以相同的方式工作,但每个元素也mutates并返回它,如:

var myArray = [1, 2, 3];
myArray = myArray.map(
  (item) => {
    return item + 1;
  }
);

console.log(myArray); // [2, 3, 4]
6
Ante Jablan Adamović

使用ES6循环 解构扩展运算符

虽然一些javascript退伍军人可能认为它很混乱,但是小辈或其他一些人可能觉得它很有用,因此对于ES6的新手来说,解构和扩展运算符的使用已被证明是非常有用的。

以下示例将使用 for...of statement和 .forEach method。

实施例6,7和8 可与像.map任何官能环路被使用,.filter.reduce.sort.every.some,以获取更多信息有关这些方法检查出 Array对象

示例1: 正常for...of循环 - 这里没有技巧。

let arrSimple = ['a', 'b', 'c'];

for (let letter of arrSimple) {
  console.log(letter);
}

示例2: 将单词拆分为字符

let arrFruits = ['Apple', 'orange', 'banana'];

for (let [firstLetter, ...restOfTheWord] of arrFruits) {
  // Create a shallow copy using the spread operator
  let [lastLetter] = [...restOfTheWord].reverse();
  console.log(firstLetter, lastLetter, restOfTheWord);

}

示例3: 使用keyvalue循环 

// let arrSimple = ['a', 'b', 'c'];

// Instead of keeping an index in `i` as per example `for(let i = 0 ; i<arrSimple.length;i++)`
// this example will use a multi-dimensional array of the following format type: 
// `arrWithIndex: [number, string][]`

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Same thing can be achieved using `.map` method
// let arrWithIndex = arrSimple.map((i, idx) => [idx, i]);

// Same thing can be achieved using `Object.entries`
// NOTE: `Object.entries` method doesn't work on internet Explorer unless it's polyfilled
// let arrWithIndex = Object.entries(arrSimple);

for (let [key, value] of arrWithIndex) {
  console.log(key, value);
}

示例4: 获取对象属性内联

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];

for (let { name, age: aliasForAge } of arrWithObjects) {
  console.log(name, aliasForAge);
}

示例5: 获取所需内容的深层对象属性

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

for (let { name, tags: [firstItemFromTags, ...restOfTags] } of arrWithObjectsWithArr) {
  console.log(name, firstItemFromTags, restOfTags);
}

示例6: 示例3 .forEach一起使用

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Not to be confused here, `forEachIndex` is the real index
// `mappedIndex` was created by "another user", so you can't really trust it

arrWithIndex.forEach(([mappedIndex, item], forEachIndex) => {
  console.log(forEachIndex, mappedIndex, item);
});

例7: 例4 .forEach一起使用

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];
// NOTE: Destructuring objects while using shorthand functions 
// are required to be surrounded by parenthesis
arrWithObjects.forEach( ({ name, age: aliasForAge }) => {
  console.log(name, aliasForAge)
});

例8: 例5 .forEach一起使用

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

arrWithObjectsWithArr.forEach(({
  name,
  tags: [firstItemFromTags, ...restOfTags]
}) => {
  console.log(name, firstItemFromTags, restOfTags);
});

6
darklightcode

Lambda语法通常不适用于IE 10或更低版本。

我经常使用

[].forEach.call(arrayName,function(value,index){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});


If you are a jQuery Fan and already have a jQuery file running, you should reverse the positions of the index and value parameters

$("#ul>li").each(function(**index,value**){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});
5
Murtuza Husain

你可以打电话给每个人这样:

let Array = [1,3,2];

theArray.forEach((element)=>{ 
  // use the element of the array
  console.log(element) 
}

element将具有从0到数组长度的每个索引的值。

输出:

1    
3    
2

阐释:

forEach是原型类。你也可以把它称为theArray.prototype.forEach(...);

原型: https://hackernoon.com/prototypes-in-javascript-5bba2990e04b

您还可以像这样更改数组:

for(let i=0;i<theArray.length;i++){
  console.log(i); //i will have the value of each index
}
4
Nouman Dilshad

如果你想用箭头函数循环遍历对象数组:

let arr=[{name:'john',age:50},{name:'clark',age:19},{name:'mohan',age:26}];

arr.forEach((person)=>{
  console.log('i am '+person.name+' and i am '+person.age+ ' old');
})
3
subhashish negi

摘要:

在遍历数组时,我们通常可能希望实现以下目标之一:

  1. 我们想迭代数组并创建新数组:

    Array.prototype.map 

  2. 我们想迭代数组而不创建新数组:

    Array.prototype.forEach 

    for..of loop

在JS中,有很多方法可以实现这两个目标。但是,有些人比其他人更有条理。下面你可以找到一些常用的方法(最方便的imo)来完成javascript中的数组迭代。

创建新数组:Map

map()是一个位于Array.prototype上的函数,它可以转换数组的每个元素,然后返回一个 new 数组。 map()将回调函数作为参数,并按以下方式工作:

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

let newArr = arr.map((element, index, array) => {
  return element * 2;
})

console.log(arr);
console.log(newArr);

我们作为参数传递给map()的回调对每个元素都执行了。然后返回一个与原始数组长度相同的数组。在这个新的数组元素由作为参数传入的回调函数转换为map()

map与其他循环机制(如forEachfor..of循环)之间的明显区别是map作为新数组返回并保留旧数组 (除非您明确地使用splice之类的思想操纵它)。 

另请注意,map函数的回调提供了当前迭代的索引号作为第二个参数。此外,第三个参数提供了调用map的数组。有时这些属性非常有用。

循环使用forEach

forEach是一个位于Array.prototype上的函数,它将回调函数作为参数。然后它为数组中的每个元素执行此回调函数。与map()函数相反,forEach函数不返回任何内容(undefined)。例如:

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

arr.forEach((element, index, array) => {

  console.log(element * 2);

  if (index === 4) {
    console.log(array)
  }
  // index, and oldArray are provided as 2nd and 3th argument by the callback

})

console.log(arr);

map函数一样,forEach回调提供当前迭代的索引号作为第二个参数。第三个参数也提供了调用forEach的数组。 

使用for..of循环遍历元素

for..of循环遍历数组的每个元素(或任何其他可迭代对象)。它的工作方式如下:

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

for(let element of arr) {
  console.log(element * 2);
}

在上面的例子中,element代表一个数组元素,arr是我们想要循环的数组。并不是名称element是任意的,我们可以选择任何其他名称,如'el'或更适用的声明。 

不要将for..in循环与for..of循环混淆。 for..in将循环遍历数组的所有可枚举属性,而for..of循环将仅循环遍历数组元素。例如:

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

arr.foo = 'foo';

for(let element of arr) {
  console.log(element);
}

for(let element in arr) {
  console.log(element);
}

2
Willem van der Veen

如果你有一个庞大的数组,你应该使用 iterators 来获得一些效率。迭代器是某些JavaScript集合的属性(例如 MapSetStringArray )。甚至, for..of 使用iteratorunder-the-hood。

迭代器通过让您一次一个地使用列表中的项目来提高效率,就像它们是一个流一样。使迭代器特殊的是它如何遍历集合。其他循环需要预先加载整个集合以便迭代它,而迭代器只需要知道集合中的当前位置。 

您可以通过调用迭代器的next方法来访问当前项。下一个方法将返回当前项的valueboolean以指示何时到达集合的末尾。以下是从数组创建迭代器的示例。 

使用 values() 方法将常规数组转换为迭代器,如下所示: 

    const myArr = [2,3,4]

let it = myArr.values();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

你也可以使用 Symbol.iterator 将常规数组转换为迭代器,如下所示: 

const myArr = [2,3,4]

let it = myArr[Symbol.iterator]();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

您还可以将常规array转换为 iterator ,如下所示: 

let myArr = [8, 10, 12];

function makeIterator(array) {
    var nextIndex = 0;
    
    return {
       next: function() {
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    };
};

var it = makeIterator(myArr);

console.log(it.next().value);   // {value: 8, done: false}
console.log(it.next().value);   // {value: 10, done: false}
console.log(it.next().value);   // {value: 12, done: false}
console.log(it.next().value);   // {value: undefined, done: true}

_ note _ : 

  • 迭代器本质上是穷举的。 
  • 默认情况下,对象不是iterable。在这种情况下使用 for..in 因为它不是值,而是使用键。

您可以阅读有关iteration protocolhere 的更多信息。 

1
BlackBeard
var a = ["car", "bus", "truck"]
a.forEach(function(item, index) {
    console.log("Index" + index);
    console.log("Element" + item);
})
1
John

// Looping through arrays using foreach  ES6 way

var data = new Array(1,2,3,4,5);
data.forEach((val,index) => {
    console.log("index :",index); // index
	console.log("value :", val); // value
});

0
arul prince

您可以使用forEach()API(由Javascript提供),它接受函数作为回调,并为数组中的每个元素运行一次。

https://fullstackgeek.blogspot.com/2019/01/arrays-in-javascript-part-2.html

0
Ayush Jain

我来自python,我发现这种方式更加清晰。
theArray是数组,实例是数组的元素

for(let instance of theArray)
{
    console.log("The instance",instance);
}

要么

for( instance in theArray)
{
  console.log("The instance",instance);
}

相比于:

theArray.forEach(function(instance) {
    console.log(instance);
});

但在一天结束时,两人都在做同样的事情

0
Peko Chan

如果您想以功能方式保存代码,请使用map:

theArray.map(instance => do_something);

通过这种方式,您将为将来的操作生成新数组,并将跳过任何不理想的副作用。

0
alejoko

如果你想使用forEach(),它看起来像 -

theArray.forEach ( element => {
    console.log(element);
});

如果你想使用for(),它看起来像 -

for(let idx = 0; idx < theArray.length; idx++){
    let element = theArray[idx];
    console.log(element);
}
0
Harunur Rashid