在 JavaScript 中逐个遍历数组?

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

我以为是这样的:

forEach(instance in theArray)

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

答案

TL; DR

  • 除非您将for-in防护措施,否则至少不要知道为什么它会咬您。
  • 通常最好的选择是

    • for-of循环(仅for-of ES2015 +),
    • Array#forEachspec | MDN )(或其some类似的亲戚)(仅适用于 ES5 +),
    • 一个简单的老式for循环,
    • 或者for-in有保障。

但是,还有其它更多探索,阅读...


JavaScript 具有强大的语义,可以遍历数组和类似数组的对象。我将答案分为两部分:真正数组的选项,以及仅是数组之类的东西的选项,例如arguments对象,其他可迭代对象(ES2015 +),DOM 集合,等等。

我会很快注意到,您现在可以通过将ES2015转换为 ES5,甚至在 ES5 引擎上使用 ES2015 选项。搜索 “ES2015 transpiling” /“ES6 transpiling” 以了解更多...

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

对于实际数组

您目前在ECMAScript 5 (“ES5”)中拥有三个选项,这是目前最广泛支持的版本,在ECMAScript 2015 中又添加了两个选项(“ES2015”,“ES6”):

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

细节:

1. 使用forEach及相关

在任何可以访问由 ES5 添加的Array功能(直接或使用 polyfills)的模糊现代环境(因此,不是 IE8)中,都可以使用forEachspec | MDN ):

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

forEach接受一个回调函数,以及(可选)一个在调用该回调时用作this函数的值(上面未使用)。依次为数组中的每个条目调用回调,从而跳过稀疏数组中不存在的条目。尽管我在上面只使用了一个参数,但回调函数使用以下三个参数调用:每个条目的值,该条目的索引以及对要迭代的数组的引用(以防您的函数尚未使用它) )。

除非您支持 IE8 之类的过时浏览器(截至 2016 年 9 月,NetApps 在市场中所占份额刚刚超过 4%),否则您可以在通用网页中愉快地使用forEach ,而无需填充。如果您确实需要支持陈旧的浏览器,则可以轻松完成forEach填充 / 填充(搜索 “es5 shim” 以获得多个选项)。

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

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

另外, forEach是 “遍历所有对象” 功能,但是 ES5 定义了其他一些有用的 “遍历数组并执行操作” 功能,包括:

  • every (在回调第一次返回falsefalse时停止循环)
  • some (在回调第一次返回truetrue时停止循环)
  • filter (创建一个新数组,其中包含 filter 函数返回true元素,并忽略其中返回false元素)
  • map (根据回调返回的值创建一个新数组)
  • reduce (通过重复调用回调,传入先前的值来建立一个值;有关详细信息,请参见规范;对汇总数组内容和许多其他内容很有用)
  • reduceRight (类似于reduce ,但是以降序而不是升序工作)

2. 使用简单的for循环

有时,旧方法是最好的:

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 及更高版本中,可以使 index 和 value 变量位于for循环的本地:

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

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

而且,这样做时,不仅为每次循环迭代都重新创建value而且还重新创建index ,这意味着在循环主体中创建的闭包保留对为该特定迭代创建的index (和value )的引用:

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

let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
    divs[index].addEventListener('click', e => {
        console.log("Index is: " + index);
    });
}
<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>

如果您有五个 div,则单击第一个将获得 “索引为:0”,如果单击最后一个则将为 “索引为:4”。如果你使用这个不起作用 var ,而不是let

3. 正确使用for-in

您会看到有人告诉您使用for-in ,但这不是for-in目的for-in遍历对象可枚举属性 ,而不是数组的索引。甚至在 ES2015(ES6)中也不保证顺序 。 ES2015 + 确实定义(通过向对象属性的命令[[OwnPropertyKeys]] [[Enumerate]]以及使用它们喜欢的东西Object.getOwnPropertyKeys ),但它没有定义for-in将遵循该顺序。 ( 其他答案的详细信息。)

数组上for-in的唯一实际用例是:

  • 这是一个稀疏的数组,里面巨大的空隙,或者
  • 您正在使用非元素属性,并且希望将它们包括在循环中

仅查看第一个示例:如果使用适当的保护措施,则可以使用for-in访问这些备用数组元素:

// `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 checks are
        /^0$|^[1-9]\d*$/.test(key) &&    // explained
        key <= 4294967294                // below
        ) {
        console.log(a[key]);
    }
}

请注意以下三个检查:

  1. 该对象具有该名称的自身属性(不是从其原型继承属性),并且

  2. 该键是所有十进制数字(例如,正常的字符串形式,而不是科学计数法),并且

  3. 该键的值在被强制为数字时为 <= 2 ^ 32-2(即 4,294,967,294)。这个数字从哪里来?它是规范中数组索引定义的一部分。其他数字(非整数,负数,大于 2 ^ 32-2 的数字)不是数组索引。它的 2 ^ 32 的理由 - 2是使最大的指数值的一个大于 2 ^ 32 下 - 1,这是一个数组的最大值length可以有。 (例如,数组的长度适合于 32 位无符号整数。) (向 RobG 表示支持,在我的博客文章的评论中指出我先前的测试不太正确。)

当然,您不会在内联代码中执行此操作。您将编写一个实用程序函数。也许:

// Utility function for antiquated environments without `forEach`
var hasOwn = Object.prototype.hasOwnProperty;
var rexNum = /^0$|^[1-9]\d*$/;
function sparseEach(array, callback, thisArg) {
    var index;
    for (var key in array) {
        index = +key;
        if (hasOwn.call(a, key) &&
            rexNum.test(key) &&
            index <= 4294967294
            ) {
            callback.call(thisArg, array[key], index, array);
        }
    }
}

var a = [];
a[5] = "five";
a[10] = "ten";
a[100000] = "one hundred thousand";
a.b = "bee";

sparseEach(a, function(value, index) {
    console.log("Value at " + index + " is " + value);
});

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

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

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

在幕后,它从数组中获取一个迭代器并循环遍历,从而从中获取值。这不存在使用for-in问题,因为它使用由对象(数组)定义的迭代器,并且数组定义其迭代器通过其 (而不是其属性)进行迭代。与 ES5 中的for-in不同for-in访问条目的顺序是其索引的数字顺序。

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

有时,您可能想显式使用迭代器。您也可以这样做,尽管它比for-of笨拙得多。看起来像这样:

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

迭代器是与规范中的迭代器定义匹配的对象。每次调用时,其next方法都会返回一个新的结果对象 。结果对象具有一个属性done ,它告诉我们是否已完成,以及一个具有该迭代值的属性value 。 (如果为false ,则done是可选的;如果undefined ,则value是可选的。)

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

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

对于类似数组的对象

除了真正的数组之外,还有一些类似数组的对象,它们具有length属性和带有数字名称的属性: NodeList实例, arguments对象等。我们如何遍历它们的内容?

对数组使用上面的任何选项

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

  1. 适用于forEach及相关(ES5 +)

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

    假设您要在NodechildNodes属性上使用forEach 。您可以这样做:

    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. 使用简单的for循环

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

  3. 使用for-in 正确

    具有与数组相同的保护措施的for-in应适用于类似数组的对象;上面#1 中由主机提供的对象的警告可能适用。

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

    for-of将使用对象提供的迭代器(如果有);我们将不得不看一下它如何与各种类似数组的对象一起运行,尤其是主机提供的对象。例如,来自querySelectorAllNodeList规范已更新为支持迭代。来自getElementsByTagNameHTMLCollection规范不是。

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

    参见#4,我们必须看看迭代器如何发挥作用。

创建一个真实的数组

其他时候,您可能希望将类似数组的对象转换为真正的数组。做到这一点非常容易:

  1. 使用数组的slice方法

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

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

    因此,例如,如果我们想将NodeList转换为一个真实的数组,我们可以这样做:

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

    请参阅下面的警告,了解主机提供的对象 。特别要注意的是,这将在 IE8 及更早版本中失败,这不能让您像this使用主机提供的对象。

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

    还可以将 ES2015 的扩展语法与支持此功能的 JavaScript 引擎一起使用:

    var trueArray = [...iterableObject];

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

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

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

    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函数与主机提供的类似数组的对象(DOM 列表和浏览器而不是 JavaScript 引擎提供的其他东西)一起使用,则需要确保在目标环境中进行测试以确保主机提供了对象的行为正常。 大多数 (现在) 确实表现正常 ,但是测试很重要。原因是您可能要使用的大多数Array.prototype方法都依赖于主机提供的对象,该对象为抽象[[HasProperty]]操作提供了诚实的答案。在撰写本文时,浏览器在这方面做得很好,但是 5.1 规范确实允许由主机提供的对象可能不诚实。在§8.6.2 中 ,该部分开头附近的大表下方的几段中),其中表示:

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

(我在 ES2015 规范中找不到等效的术语,但情况肯定仍然如此。)同样,在撰写本文时,现代浏览器中常见的由主机提供的类似数组的对象 [例如NodeList实例] 可以正确处理[[HasProperty]] ,但进行测试很重要。)

注意 :此答案已过时。对于更现代的方法,请查看array 上可用的方法 。感兴趣的方法可能是:

  • 每次
  • 地图
  • 过滤
  • 压缩
  • 降低
  • 每一个
  • 一些

迭代以阵列的标准方法的 JavaScript是香草for -loop:

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

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

ECMAScript 5 中 ,数组原型上将有一个 forEach 方法,但是旧版浏览器不支持该方法。因此,要能够始终如一地使用它,您必须具有一个支持它的环境(例如,服务器端 JavaScript 的Node.js ),或使用 “Polyfill”。但是,用于此功能的 Polyfill 很简单,并且由于它使代码更易于阅读,因此它是一个很好的 polyfill。

如果您使用的是jQuery库,则可以使用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].
}

向后循环

我认为反向 for 循环在这里值得一提:

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

优点:

  • 您不需要声明临时的len变量,也不需要在每次迭代时与array.length进行比较,这两者都可能需要一分钟的优化。
  • 以相反的顺序从 DOM 中删除兄弟姐妹通常更为有效 。 (浏览器需要减少其内部数组中元素的移动。)
  • 如果在循环时,在索引i处或索引i之后修改数组 (例如,在array[i]处删除或插入项目),则正向循环将跳过向左移到位置i 的项目 ,或重新处理i右移的项目。在传统的 for 循环中,您可以更新i以指向需要处理的下一个项目 - 1,但是简单地反转迭代方向通常是一种更简单更优雅的解决方案
  • 同样,在修改或删除嵌套的 DOM 元素时,反向处理可以避免错误 。例如,考虑在处理父节点的子节点之前修改其内部 HTML。到子节点到达时,它将与 DOM 分离,在编写父节点的 innerHTML 时已被新创建的子节点替换。
  • 与其他可用选项相比,它的键入和读取 更短 。尽管for ... of forEach()和 ES6 而输for ... of

缺点:

  • 它以相反的顺序处理项目。如果您要根据结果构建新的数组,或在屏幕上打印内容,则自然相对于原始顺序反转输出
  • 重复将兄弟姐妹作为第一个孩子插入 DOM 中以保持其顺序效率较低 。 (浏览器将不得不保持正确的状态。)为了高效,有序地创建 DOM 节点,只需像往常一样循环并追加(并使用 “文档片段”)即可。
  • 反向循环会使初级开发人员感到困惑 。 (根据您的前景,您可能会认为这是一种优势。)

我应该一直使用吗?

某些开发人员默认情况下使用反向 for 循环,除非有充分的理由进行向前循环。

尽管性能提升通常微不足道,但仍会引起尖叫:

“只需对列表中的每个商品都这样做,我不在乎订单!”

然而在实践中,实际上并不是意图可靠的指示,因为它是从这些场合没有区别,当你对的井井有条,而且确实需要循环反向。因此,实际上,将需要另一种构造来准确表达 “不在乎” 的意图,这在大多数语言(包括 ECMAScript)中目前尚不可用,但是可以将其称为例如forEachUnordered()

如果顺序无关紧要,并且效率是一个问题(在游戏或动画引擎的最内部循环中),则可以将反向 for 循环用作您的首选模式。只要记住,在现有代码看到反向 for 循环并不一定意味着顺序无关紧要!

最好使用 forEach()

通常,对于更关注透明度和安全性的高级代码,我以前建议使用Array::forEach作为循环的默认模式(尽管这些天我更喜欢使用for..of )。选择forEach而不是反向循环的原因是:

  • 阅读起来更清晰。
  • 这表明, 是不会来的块中移动(这始终是一个可能的惊喜躲在长forwhile循环)。
  • 它为您提供了关闭的自由范围。
  • 它减少了局部变量的泄漏以及与外部变量的意外碰撞(以及外部变量的突变)。

然后,当您在代码中确实看到反向 for 循环时,这表明它是有充分原因(也许是上述原因之一)被反向的。看到传统的 forward for 循环可能表明可以进行移位。

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

现在,更好地用于...。

关于for..offorEach()是否for..of存在争议:

  • 为了获得最大程度的浏览器支持, for..of 需要为迭代器使用for..of ,这会使您的应用执行速度稍慢,下载速度稍大。

  • 因此(并鼓励使用mapfilter ), 一些前端样式指南完全禁止使用for..of

  • 但是上述问题不适用于 Node.js 应用程序,因为for..of现在得到了很好的支持。

  • 而且此外, awaitforEach()内部不起作用 。在这种情况下,使用for..of最清晰的模式

就个人而言,我倾向于使用最容易阅读的外观,除非性能或尺寸缩小已成为主要问题。因此,这些天我更喜欢使用for..of而不是forEach() ,但是在适用时,我将始终使用mapfilterfindsome 。 (为了我的同事,我很少使用reduce 。)


它是如何工作的?

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

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

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

  • 它如何从array.length开始而不爆炸?

    因为i-- 每次迭代之前运行,所以在第一次迭代中,我们实际上将访问array.length - 1的项,这避免了Array-bound-of-bounds undefined项的任何问题。

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

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

    诀窍是,不像--i ,尾随i--运营商递减i却产生了递减之前的值。您的控制台可以证明这一点:

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

    [5, 5, 4]

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

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


琐事

有些人喜欢在反面for循环画一个小箭头,并以眨眼结束:

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

感谢 WYL 向我展示反向 for 循环的好处和恐惧。

一些C风格的语言使用foreach枚举。在 JavaScript 中,这是通过for..in循环结构完成的

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

有一个陷阱。 for..infor..in对象的每个可枚举成员,以及其原型上的成员。为了避免读取通过对象原型继承的值,只需检查属性是否属于对象:

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

此外, ECMAScript 5Array.prototype添加了一个forEach方法,该方法可用于使用回溯对数组进行枚举(polyfill 在文档中,因此您仍可将其用于旧版浏览器):

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

重要的是要注意,当回调返回false时, Array.prototype.forEach不会中断。 jQuery 的Underscore.js提供自己的变化each提供可短路回路。

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

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

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

我知道这是一篇旧文章,并且已经有很多不错的答案。为了更加完整,我想我会使用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函数与内置的香草味for循环相比有优缺点。

优点

  • 易读
  • 易写性
  • 如果可用, angular.forEach将使用 ES5 forEach 循环。现在,我会去的利弊部分 efficientcy,作为 foreach 循环比 for 循环慢得多。我将其作为专业人士提及是因为保持一致和标准化非常好。

考虑以下两个嵌套循环,它们执行的功能完全相同。假设我们有 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
        }
    }
}

诚然,这是一个非常简单的假设的例子,但我已经写了三重嵌入使用第二种方法循环,这是非常难以阅读,并为此事写。

缺点

  • 效率。 angular.forEach ,和本地forEach ,对于这个问题,都是这样比正常的慢得多for循环.... 约90%的速度较慢 。因此,对于大型数据集,最好坚持使用本机的for循环。
  • 没有中断,继续或返回支持。 continue实际上是由 “支持意外 ”,继续在angular.forEach你简单的把一个return;函数中的语句,例如angular.forEach(array, function(item) { if (someConditionIsTrue) return; });这将导致它继续执行该迭代的功能。这也是由于本机forEach不支持 break 或forEach的事实。

我敢肯定,还有其他各种利弊,请随时添加您认为合适的任何内容。我认为,最重要的是,如果您需要效率,请坚持使用本机for循环来满足您的循环需求。但是,如果您的数据集较小,并且为了交换可读性和可写性而放弃某种效率是可以的,那么angular.forEach那个坏男孩扔个angular.forEach

如果您不介意清空数组:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

x将包含y的最后一个值,并将其从数组中删除。您还可以使用shift()来从y删除第一项。

一个forEach实现( 请参阅 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
  }
);

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

CodePen如何工作的示例是:

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

也可以看看