我如何可以循环使用 JavaScript 数组中的所有对象?

我认为是这样的 (其中的对象是我的对象的数组)︰

forEach(instance in objects)

但这似乎不是正确的。

2012-02-17 13:51:48
问题评论:

我未查看它,但我寻找forEach并不只是for如所述,在 c# 中它是有点不同,并且,:) 混淆我

Array.ForEach 是慢于 for() 中每个数组的 JavaScript 中约 95%。查看此性能在线测试︰ 通过coderwall.com/p/kvzbpa jsperf.com/快速-阵列 foreach

多数情况下慢 95%不打算将大量blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html

回答:

TL; 灾难恢复

  • 不要使用for-in除非您使用安全措施,或至少意识到为什么它可能的隐患。
  • 通常是您的最佳匹配

    • for-of循环 (ES2015 + 只),
    • Array#forEach(spec| MDN)(或其亲属some等)(ES5 + 只),
    • 简单传统for循环,
    • for-in的安全措施。

但还有很多更多以研究、 阅读...


JavaScript 有强大的语义来循环遍历数组和类似数组的对象。我已拆分为两个部分的答案︰ 选项对于正版的数组,并就阵列的,如arguments对象、 其他 iterable 对象 (ES2015 +)、 DOM 集合,等那些对象的选项。

我将快速地请注意,您可以使用 ES2015 选项现在,即使在 ES5 引擎上的transpiling ES2015 对 ES5。搜索"ES2015 transpiling"/"ES6 transpiling"的详细信息...

没关系,让我们看一下我们的选项︰

对于实际的数组

ECMAScript 5 ("ES5"),此刻,最广泛支持的版本中有三个选项,很快便会具有两个ECMAScript 2015 ("ES2015","ES6"),从事供应商支持的 JavaScript 的最新版本中︰

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

详细信息︰

1.使用forEach及相关

如果您使用支持 ES5 的Array功能的环境 (直接或使用的填充程序),您可以使用新的forEach (spec| MDN ):

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

forEach接受迭代器函数和值要用作this调用迭代器函数 (不使用以上) 时,(可选)。迭代器函数跳过稀疏数组中的不存在项的顺序的数组,每个条目。虽然我只使用一个以上的参数,与三个调用迭代器函数︰ 每一个条目,该条目,并参考 (如果您的函数没有它方便) 要循环访问的数组的索引的值。

仍 (截至 3 月 2014) 通用的 web 页上使用forEach要求包含一个"填充程序"为其浏览器不支持它本身,因为 IE8 和前面不让它 (和那些习惯通过某处 7%和 21%,这取决于您认为谁的全局浏览器用户之间; 该图通过在中国显著提高与其他地方有点倾斜始终进行检查以查看您需要支持自己统计)。但一样代码轻松地完成 (搜索的几个选项的"es5 填充程序")。

forEach具有不需要索引和值在中声明变量包含的范围,因为它们作为迭代函数中,参数提供并因此可以很好地作用于就该迭代的好处。

如果您担心使每个数组项的函数调用的运行时开销,不是;详细信息.

此外, forEach "循环通过所有这些"函数,但 ES5 定义几个其他有用"努力通过阵列和做事情"功能,包括︰

  • every(停止循环的迭代器返回false或 falsey 是第一次)
  • some(停止循环的迭代器返回true或 truthy 是第一次)
  • 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 引擎,很少需要达到汁,最后一位。

3.使用for-in正确

您将收到通知您要使用的人员for-in,但不是什么for-infor-in循环访问可枚举对象的属性,不是数组的索引。订单不能保证,甚至不在 ES2015 (ES6)。ES2015 没有定义顺序对对象属性 (通过[[OwnPropertyKeys]] [[Enumerate]],并使用它们与Object.getOwnPropertyKeys类似的事情),但它却没有定义,for-in将遵循该顺序。此其他答案(详细信息.)

尽管如此,它很有用,特别是对于canStill, it be useful, particularly for sparse arrays, if you use appropriate safeguards:

// `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. 对象具有其自己的属性,该名称 (而不是它继承自其原型),和

  2. 它的关键是普通字符串形式的十进制数字字符串,其值为 < = 2 ^32-2 (即 4294967294)。该数字来自的地方?它的数组索引规范中定义的一部分。其他数字 (非整数,负数,数字大于 2 ^32-2) 不是数组的索引。原因在于 2 ^32- 2是,所做的最大索引值一个低于 2 ^32- 1,这就是数组的length可具有的最大值。(例如,数组的长度所能容纳的 32 位无符号整数。)(与 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 向 JavaScript 的迭代器使用迭代器的最简单办法是新for-of语句。它看起来像这样︰

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

输出︰

a
b
c

下机盖,从阵列和循环的迭代器获取通过它,正从其获取值。这没有问题,使用for-in有,因为它使用对象 (阵列) 所定义的迭代器和阵列定义其迭代器遍历其条目(而不是它们的属性)。for-in在 ES5,在其中访问这些项的顺序是他们的索引的数字顺序。

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

有时,您可能需要使用迭代器显式您可以这样做,太,虽然很多 clunkier 比for-of它看起来像这样︰

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

迭代器返回一个新的对象每次调用next函数 (具体而言,一个生成器)。由迭代器返回的对象具有属性、done、 告诉我们是否完成,和为该迭代的值的属性value

value的含义取决于迭代器;数组返回迭代器的 (至少) 有三个功能提供支持︰

  • values(): 这是我用上面。它返回一个迭代器,其中每个value都为该迭代的值。
  • keys()︰ 返回一个迭代器,其中每个value都为该迭代 (这样我们a上面,基数为"0",则"1",然后"2").
  • entries()︰ 返回迭代器,其中每个value是在窗体中[key, value]为该迭代数组。

(截至撰写本文时,Firefox 29 支持entrieskeys,但不是values.)

对于类似数组的对象

除了真正的数组,也有类似数组的 length属性和属性指定与数值名称的对象︰NodeList实例arguments对象,等等。我们如何循环访问其内容?

使用上面的选项数组

在至少一些,和可能是大多数甚至所有阵列经常接近上述应用同样也为类似数组的对象︰

  1. 使用forEach和相关的 (ES5 +)

    Array.prototype上的各种功能是"有意一般",通常可以通过Function#call的类似数组的对象上使用或Function#apply(请参阅宿主提供的对象的注意末尾的此答案,但这是一个罕见的问题。)

    假设您要使用forEach NodechildNodes属性。可以实现此目的︰

    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将使用迭代器所提供的对象 (如果有的话);我们必须看到这如何播放不同的类似数组的对象,特别是提供主机的。

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

    请参见 #4,我们不得不看到迭代器播放的方式。

创建一个数组,则返回 true

其他时候,您可能需要将类似数组的对象转换为一个数组,则返回 true。做的是非常的简单︰

  1. 使用数组的slice方法

    我们可以使用slice方法的数组,它像上面提到的其他方法是"有意通用",并因此可以用类似于数组的对象,如下︰

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

    例如,如果我们想要将NodeList转换为一个数组,则返回 true,我们可以这样做︰

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

    请参阅宿主提供的对象的需要说明下。特别是,请注意,此操作将失败在 IE8 中以及更早版本,它不允许您这样为this使用宿主提供的对象。

  2. 使用范围运算符 (...

    也可能使用支持此功能的 JavaScript 引擎传播运算符ES2015,它是︰

    var trueArray = [...iterableObject];
    

    例如,如果我们想要将NodeList转换为一个数组,则返回 true,跨页语法这变得非常简洁︰

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

    Array.from (ES2015,但 shimmable) 从一个类似数组的对象,选择第一次传递的条目通过映射函数创建数组。因此︰

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

为宿主提供的对象的注意事项

如果宿主提供的类似数组的对象 (DOM 列表和其他事项提供的浏览器,而不是 JavaScript 引擎) 使用Array.prototype函数,您需要确保测试,以确保主机提供对象的正确行为的目标环境中。最不要采取正确行为(现在),但很重要测试。原因是,您很可能想要使用Array.prototype方法中的大多数依赖于宿主提供对象提供抽象的[[HasProperty]]操作的真实答案。在撰写本文时,浏览器干的非常好的但规范允许的可能性 ES5 宿主提供的对象可能不诚实;它是在§8.6.2 (该节的开头附近大表下方的几个段落),在那里显示了︰

主机对象可能实现这些内部方法以任何方式除非指定,否则;例如,一种可能是[[Get]]和特定的主机对象[[Put]]确实提取和存储属性值但[[HasProperty]]总是生成false.

(在 ES2015 规范中,没有找到相同的措辞,但它已绑定到仍出现这种情况)。再次,本书截稿时常见的宿主提供的类似数组的对象在现代浏览器中 (例如NodeList实例)执行 [[HasProperty]]正确处理,但务必要测试。

我想要添加的.forEach高效地不能中断。您必须引发异常来执行中断。

@Pius︰ 如果要中断该循环,您可以使用some(我允许破坏以及forEach首选,但他们,嗯,没有邀请我。;-))

@T.J.Crowder 真,尽管看上去更像一种解决因为不是主要目的。

如果您使用的 ie8 可以粘贴此代码以使用 forEach。如果 (!Array.prototype.forEach) {Array.prototype.forEach = 功能 (fn,范围) {对于 (var 我 = 0,长度 = this.length; 我 < 长度; + + 我) {fn.call (范围、 此 [i],i,此);}};}...developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/

@CharlesTWall3︰ 这就是forEach严重不正确的实现。这里重要的一课︰ 只是因为它位于 MDN 时,并不意味着它是很好。

编辑︰ 此答案是千辛万苦过时。采用更现代的方法,查看可用的数组的方法感兴趣的方法可能是︰

  • forEach
  • 地图
  • 筛选器
  • 压缩
  • 减少
  • 每个
  • 某些

循环访问数组在JavaScript中的标准方法是香草for的循环︰

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

但是请注意,此方法才好是否您有密集的数组中,并且每个索引占用的元素。如果数组是稀疏的然后可能遇到的性能存在数组中使用此方法,因为您将遍历索引确实做了大量的问题。在这种情况下,for .. in-循环可能是个更好的想法。但是,必须使用相应的保护措施以确保,只有数组 (也就是说,数组元素) 所需的属性采取行动,因为for..in-循环还可枚举在旧版浏览器,或者如果其他属性定义为enumerable.

ECMAScript 5中将有一个 forEach 方法数组原型中,在但旧式浏览器不支持它。因此为了能够以一致的方式使用它必须具有支持 (例如, Node.js为在服务器端 JavaScript),它的环境或使用"填充代码"。填充代码实现此功能,但是,是微不足道,因为这可以使代码更易读,很好的填充代码,包括。

为什么for(instance in objArray)不是正确的用法?它看起来更简单,对我来说,但我听见您说它的用法不正确的方法呢?

您可以使用内联长度缓存︰ 为 (var i = 0,l = arr.length; 我 < l; i + +)

是有意的第一行末尾的逗号,还是输入错误 (可能是分号)?

@wardha 网站是故意。它让我们能够用来声明多个变量单个var的关键字。如果我们必须使用分号,然后element已声明了在全局范围中 (或者,相反,JSHint 将有 screamed 在我们之前它已经达到生产)。

如果您正在使用jQuery库,则可以使用jQuery.each :

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

编辑︰

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

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

我可能将最常使用此答案。它不是最好的解决问题,但在实践中,将会是最简单、 最适用于那些我们使用 jQuery。我认为我们应该全部了解香草方式也不过。也无妨展开您的理解。

它只是为了︰ 每个 jQuery 是更慢,然后本机的解决方案。建议通过 jQuery 时就有可能,而不是 jQuery 使用本机的 JavaScript。jsperf.com/browser-diet-jquery-each-vs-for-loop

避免使用 jQuery 时可以使用香草 js

坚持标准的 JS,除非没有本机语言解决方案保持答案从第三方函数库

有点让我想起这︰ i.stack.imgur.com/ssRUr.gif

某些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.prototypeforEach方法︰

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

请务必注意回调返回false时,不会分页Array.prototype.forEachjQueryUnderscore.js提供了有关each提供可以 short-circuited 的循环自己变体。

那么如何一个不会将外出时的 ECMAScript5 foreach 循环像我们将用于正常循环或 foreach 循环类似的发现在 C 样式语言?

@CiaranG,在 JavaScript 是经常可以看到each方法,这些方法return false以用于中断forEach循环,但这并不是一个选项。无法使用外部标志 (即if (flag) return;但它将仅阻止函数体的其余部分执行, forEach仍会继续遍历整个集合。

向后循环

我认为反向循环值得注意︰

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

优点︰

  • 您不需要声明len的临时变量,或比较array.length在每次迭代,或者其中可能每分钟优化。
  • 按相反的顺序删除兄弟从 DOM 是通常效率更高(浏览器需要做较少转移其内部数组中的元素。)
  • 如果您修改该数组循环时间或索引i (例如您删除或插入的项在array[i]) 之后, 再转发循环将跳过该项目的同时,将移到左i,或重新处理已向右位移的i个项目。在传统的 for 循环,您可以更新为指向下一个项目需要处理的 1,但只反转方向通常是迭代的更简单更简洁的解决方案.
  • 同样,如果修改或删除嵌套的 DOM 元素,按相反的顺序处理可以避免错误例如,请考虑在处理其子之前修改的父节点 innerHTML。到达子节点时它将分离从 DOM 中,父母的 innerHTML 编写时遇到由新创建的子。
  • 它是类型,和阅读,比一些可用的其他选项。尽管它将失去对forEach()和 ES6 的for ... of.

缺点︰

  • 它按相反的顺序处理的项。如果生成的结果,或在屏幕上的打印操作,自然相对于原始订单将冲销输出从一个新的数组。
  • 重复插入同辈 DOM 作为第一个子为了保留它们的顺序是效率较低(浏览器将保留无需移动内容右。)若要有效地按顺序创建 DOM 节点,只需循环转发和追加按正常 (和还使用"文档片段")。
  • 反向循环是令人困惑的初级开发。(您可能考虑的一项优势,具体取决于您的 outlook。)

应始终使用它?

有些开发人员使用反向循环,默认情况下,除非有合理的理由在转发循环。

虽然获得的性能通常并不显著,但其排序的 screams:

"仅仅执行此到每个项目在列表中,我并不关心该订单 !"

但是实际上是实际的可靠表示的意图,因为它在这些情况下,当您不要关心有关订单,和真正的没有区别做需要反向循环。所以事实上另一构造需要能够准确地表示"不关心"的意图,在大多数语言中,此情况下,但它可以调用,例如, forEachUnordered()包括当前不可用的内容.

如果顺序并不重要,而提高效率是一个问题 (在游戏或动画引擎的内层循环),则可能是用于反向循环去模式作为可接受。只记得看到 for 循环中现有的代码并不意味着相反的顺序无关 !

最好使用 forEach()

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

  • 它是清除读取。
  • 它表明,不打算在块内移动 (这始终可能意外隐藏在长forwhile循环。)
  • 它为您提供免费范围的闭包。
  • 它减少了漏出的局部变量和意外与冲突 (以及变化的) 外部变量。
  • 但请注意,与不同的mapforEach 排除循环将出一部分中途中断的可能性。这是一种功能,可以通过从该回调中返回false触发。

然后当您看到 for 循环反向在代码中,是一个很好的理由 (可能是由于上述原因之一) 相反的提示。并看到传统的正向循环可能表明移位可以采取的地方。

(如果意图进行的讨论没有意义给您,然后您和您的代码可以受益观看 Crockford 的编程样式和您的大脑上的演讲.)


它是如何工作的?

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避免了数组超出界限undefined项目的任何问题的项目。

  • 为什么它不会停止在索引 0 前循环?

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

    诀窍就在于不同--i,尾随i--运算符递减i ,而结果值之前减量。控制台可以说明这点︰

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

    [5, 5, 4]

    等最终的迭代,以前是1i--表达式更改为0 ,而实际上的结果1 (truthy),并因此条件将传递。在下一次迭代i-- i变成-1 ,而结果0 (falsey),从而导致执行立即从循环的底部放。

    在传统的循环时, i++转发和++i(如道格拉斯 • Crockford 指出) 互换。但是反向使用 for 循环,因为我们减量也是条件表达式中,我们必须坚持用i--如果我们想要处理的项位于索引 0 处。


琐事

一些人喜欢画中的反向for循环和尾端和传情动漫小箭头︰

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

片尾显示我的优点和怪物战斗的 for 循环反向前往 WYL。

我忘了添加基准测试我还忘了提及如何反向循环是像 6502,8 位处理器优化的重要在您真正获得比较免费 !

这样如何反向循环的?var i = array.length;while(i--) {...

很抱歉我是 AFK 的 @Kabb5。是该循环是非常明确、 本质上是相同的。

内容来源于Stack Overflow For-each over an array in JavaScript?
请输入您的翻译

For-each over an array in JavaScript?

确认取消