For-each在JavaScript中的数组?

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

我以为它是这样的:

forEach(instance in theArray)

theArray我的阵列在哪里,但这似乎是不正确的.

回答

TL; DR

  • for-in除非您使用保护措施或至少知道它为什么会咬你,否则不要使用.
    • 一个for-of环(ES2015 +只),
    • Array#forEach(spec| MDN)(或其亲属some等)(仅限ES5 +),
    • 一个简单的老式for循环,
    • for-in有保障措施.

但是,还有很多更多的探索,阅读...


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

我会很快注意到,您可以使用ES2015选项现在,即使是在ES5引擎,通过transpiling ES2015到ES5.搜索"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和相关

在任何模糊的现代环境(因此,不是IE8)中,您可以访问ArrayES5添加的功能(直接或使用polyfill),您可以使用forEach(spec| 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(第一次回调返回时停止循环false或出现错误)
  • some(第一次回调返回时停止循环true或者某些事情)
  • 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及更高版本中,您可以将索引和值变量设置为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"

And when you do that, not just value but also index is recreated for each loop iteration, meaning closures created in the loop body keep a reference to the index (and value) created for that specific iteration:

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"
}

If you had five divs, you'd get "Index is: 0" if you clicked the first and "Index is: 4" if you clicked the last. This does not work if you use var instead of let.

3. Use for-in correctly

You'll get people telling you to use for-in, but that's not what for-in is for. for-in loops through the enumerable properties of an object, not the indexes of an array. The order is not guaranteed, not even in ES2015 (ES6). ES2015 does define an order to object properties (via [[OwnPropertyKeys]], [[Enumerate]], and things that use them like Object.getOwnPropertyKeys), but it does not define that for-in will follow that order. (Details in this other answer.)

Still, it can be useful, particularly for sparse arrays, if you use appropriate safeguards:

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

Note the two checks:

That's a tiny bit of added overhead per loop iteration on most arrays, but if you have a sparse array, it can be a more efficient way to loop because it only loops for entries that actually exist. E.g., for the array above, we loop a total of three times (for keys for-in, length, and for-of — remember, they're strings), not 10,001 times.

Now, you won't want to write that every time, so you might put this in your toolkit:

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

And then we'd use it like this:

<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>

Or if you're interested in just a "good enough for most cases" test, you could use this, but while it's close, it's not quite correct:

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

4. Use for-of (use an iterator implicitly) (ES2015+)

ES2015 adds iterators to JavaScript. The easiest way to use iterators is the new for-in statement. It looks like this:

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

Output:

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

Under the covers, that gets an iterator from the array and loops through it, getting the values from it. This doesn't have the issue that using for-in has, because it uses an iterator defined by the object (the array), and arrays define that their iterators iterate through their entries (not their properties). Unlike for-of in ES5, the order in which the entries are visited is the numeric order of their indexes.

5. Use an iterator explicitly (ES2015+)

Sometimes, you might want to use an iterator explicitly. You can do that, too, although it's a lot clunkier than next. It looks like this:

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

The iterator is an object matching the Iterator definition in the specification. Its done method returns a new result object each time you call it. The result object has a property, value, telling us whether it's done, and a property done with the value for that iteration. (false is optional if it would be value, undefined is optional if it would be value.)

The meaning of values() varies depending on the iterator; arrays support (at least) three functions that return iterators:

  • value: This is the one I used above. It returns an iterator where each "a" is the array entry for that iteration ("b", "c", and keys() in the example earlier).
  • value: Returns an iterator where each a is the key for that iteration (so for our "0" above, that would be "1", then "2", then entries()).
  • value: Returns an iterator where each [key, value] is an array in the form length for that iteration.

For Array-Like Objects

Aside from true arrays, there are also array-like objects that have a NodeList property and properties with numeric names: arguments instances, the forEach object, etc. How do we loop through their contents?

Use any of the options above for arrays

At least some, and possibly most or even all, of the array approaches above frequently apply equally well to array-like objects:

  1. The various functions on Function#call are "intentionally generic" and can usually be used on array-like objects via Function#apply or forEach. (See the Caveat for host-provided objects at the end of this answer, but it's a rare issue.)

    Suppose you wanted to use Node on a childNodes's for property. You'd do this:

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

    If you're going to do that a lot, you might want to grab a copy of the function reference into a variable for reuse, e.g.:

    // (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. Obviously, a simple for-in loop applies to array-like objects.

  3. for-of with the same safeguards as with an array should work with array-like objects as well; the caveat for host-provided objects on #1 above may apply.

  4. NodeList will use the iterator provided by the object (if any); we'll have to see how this plays with the various array-like objects, particularly host-provided ones. For instance, the specification for the querySelectorAll from HTMLCollection was updated to support iteration. The spec for the getElementsByTagName from slice was not.

  5. See #4, we'll have to see how iterators play out.

Create a true array

Other times, you may want to convert an array-like object into a true array. Doing that is surprisingly easy:

  1. We can use the NodeList method of arrays, which like the other methods mentioned above is "intentionally generic" and so can be used with array-like objects, like this:

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

    So for instance, if we want to convert a this into a true array, we could do this:

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

    See the Caveat for host-provided objects below. In particular, note that this will fail in IE8 and earlier, which don't let you use host-provided objects as ... like that.

  2. It's also possible to use ES2015's spread syntax with JavaScript engines that support this feature:

    var trueArray = [...iterableObject];
    

    So for instance, if we want to convert a Array.from into a true array, with spread syntax this becomes quite succinct:

    var divs = [...document.querySelectorAll("div")];
    
  3. Array.prototype (ES2015+, but easily polyfilled) creates an array from an array-like object, optionally passing the entries through a mapping function first. So:

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

    Or if you wanted to get an array of the tag names of the elements with a given class, you'd use the mapping function:

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

Caveat for host-provided objects

If you use Array.prototype functions with host-provided array-like objects (DOM lists and other things provided by the browser rather than the JavaScript engine), you need to be sure to test in your target environments to make sure the host-provided object behaves properly. Most do behave properly (now), but it's important to test. The reason is that most of the [[HasProperty]] methods you're likely to want to use rely on the host-provided object giving an honest answer to the abstract [[Get]] operation. As of this writing, browsers do a very good job of this, but the 5.1 spec did allow for the possibility a host-provided object may not be honest. It's in §8.6.2, several paragraphs below the big table near the beginning of that section), where it says:

(I couldn't find the equivalent verbiage in the ES2015 spec, but it's bound to still be the case.) Again, as of this writing the common host-provided array-like objects in modern browsers [[[HasProperty]] instances, for instance] do handle for-in correctly, but it's important to test.)

  • @Pius:如果你想打破循环,你可以使用[`some`](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.17).(我本来希望允许打破'forEach`,但他们,嗯,没有问我.;-))
  • 我还想补充一点,`.forEach`无法有效破解.你必须抛出异常来执行休息.
  • @ user889030:你需要一个`,`在`k = 0`之后,而不是`;`.记住,编程是很多东西,其中一个是密切关注细节... 🙂
  • @TJCrowder是的,即使它看起来更像是一种解决方法,因为它不是它的主要目的.
  • @JimB:上面已经介绍了(并且`length`不是方法).:-)
  • @Alex - Properties on the array that don't represent array elements. For example: `const a = ["a", "b"]; a.example = 42;` That array has three properties (other than the ones all arrays have), whose names are the strings `"0"`, `"1"`, and `"example"`. The property named `"example"` is a non-element property. The other two are element properties, because they represent the elements of the array.

编辑:这个答案绝对是过时的.要获得更现代的方法,请查看阵列上可用的方法.感兴趣的方法可能是:

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

在JavaScript中迭代数组的标准方法是vanilla for-loop:

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

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

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

  • 您可以使用内联长度缓存:for(var i = 0,l = arr.length; i <l; i ++)
  • @ wardha-Web这是故意的.它使我们能够使用单个`var`-keyword声明多个变量.如果我们使用了分号,那么`element`就会在全局范围内声明(或者说,JSHint在它到达生产之前会尖叫我们).
  • 逗号在第一行末尾是故意的,还是拼写错误(可以是分号)?
  • 为什么`for(objArray中的instance)`不是正确的用法?对我来说,它看起来更简单,但我听到您说这不是正确的使用方式吗?

如果您正在使用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].
}
  • 只是为了它:jQuery每个都比原生解决方案慢得多.jQuery建议在可能的情况下使用本机JavaScript而不是jQuery.http://jsperf.com/browser-diet-jquery-each-vs-for-loop
  • 当你可以使用vanilla js时,不要使用jQuery
  • 我可能会最经常使用此答案。这不是对这个问题的最佳答案,但实际上,对于使用jQuery的我们来说,这将是最简单,最适用的。我确实认为我们也应该学习香草方式。扩展您的理解永远不会有伤害。
  • 坚持使用标准JS,除非没有本地语言解决方案,否则不要让第三方库出局

向后循环

我认为反向循环值得一提:

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

好处:

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

缺点:

  • 它以相反的顺序处理项目.如果您从结果中构建新数组,或在屏幕上打印内容,则输出将相对于原始顺序反转.
  • 为了保留他们的顺序,反复插入兄弟姐妹作为第一个孩子的DOM 效率较低.(浏览器将不得不改变方向.)要按顺序有效地创建DOM节点,只需循环前进并正常追加(并使用"文档片段").
  • 反向循环让初级开发人员感到困惑.(您可能会认为这是一个优势,具体取决于您的前景.)

我应该经常使用它吗?

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

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

然而在实践中是不是真正意图的可靠指标,因为它是从这些场合没有区别,当你关心秩序,确实需要循环反向.因此实际上需要另一种构造来准确地表达"不关心"的意图,这种意图目前在大多数语言中都不可用,包括ECMAScript,但是可以被称为例如forEachUnordered().

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

最好使用forEach()

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

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

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

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


它是如何工作的?

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

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

  • 因为每次迭代之前for..of运行,所以在第一次迭代时我们实际上将访问该项目,避免了Array-out-of-bounds项目的任何问题.map filter

  • 当条件for..of计算为假值(当它产生0时),循环将停止迭代.

    诀窍是,不同的是for..of,尾随await运算符递减forEach()但在递减之前产生值.你的控制台可以证明:

    for..of

    for..of

    所以在最后的迭代中,之前是1并且forEach()表达式将其更改为0但实际上产生1(真实),因此条件通过.在下一次迭代中,mapi更改为-1但产生0(falsey),导致执行立即退出循环的底部.

    在循环的传统前锋中,filter并且find是可以互换的(正如Douglas Crockford指出的那样).然而在反向for循环中,因为我们的减量也是我们的条件表达式,some如果我们想要处理索引0处的项,我们必须坚持.


琐事

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

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

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

  • 我忘了添加**[基准](http://jsperf.com/loops/33)**.我也忘了提到反向循环是如何在像6502这样的8位处理器上进行重大优化,你真的可以免费获得比较!
  • 这对于反向循环怎么样?var i = array.length; 当我 - ) { ...

一些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添加了一个forEach方法Array.prototype,可以使用calback对数组进行枚举(polyfill在文档中,因此您仍然可以将它用于旧版浏览器):

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

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

  • @CiaranG,在JavaScript中,通常会看到`each`方法允许`return false`用于打破循环,但是使用`forEach`这不是一个选项.可以使用外部标志(即`if(flag)return;`,但它只会阻止函数体的其余部分执行,'forEach`仍然会继续迭代整个集合.
  • 那么如何突破一个ECMAScript5 foreach循环,就像我们对普通的for循环或像C风格语言中发现的foreach循环一样?

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

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

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

  • for(var i = 0,length = myArray.length; i <length; i ++)应该这样做
  • @EdsonMedina我道歉,我完全错了.在作业之后使用`,`确实*不*引入新的全局,所以你的建议只是**很好**!我在混淆这个问题时遇到了另一个问题:在赋值[确实创建](http://stackoverflow.com/questions/1758576/multiple-left-hand-assignment-with-javascript#1758912)之后使用`=` .
  • @EdsonMedina这也将创建一个名为`length`的新全局变量.;)
  • @joeytwiddle是的,但这超出了这篇文章的范围.无论如何,你将创建一个全局变量.

我知道这是一个老帖子,已经有很多很棒的答案了.为了更完整一点,我想我会使用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个嵌套循环,这些循环完全相同.假设我们有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不支持中断或继续.

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


如果你不介意清空数组:

var x;
while(x = y.pop()){
alert(x); //do something
}

x将包含最后一个值,y它将从数组中删除.您还可以使用shift()哪个将提供和删除第一个项目y.

  • 如果您碰巧有一个稀疏的数组,如`[1,2,undefined,3]`,它就不起作用.
  • ...或其他任何虚假的内容:[[1,2,0,3]`或`[true,true,false,true]`

一个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
}
);
  • 迭代器正在执行不必要的长度计算。在理想情况下,列表长度应仅计算一次。
  • @MIdhunKrishna我更新了我的答案和jsFiddle,但要知道它并不像您想的那么简单。检查此[问题](http://stackoverflow.com/questions/5752906/is-reading-the-length-property-of-an-array-really-that-expensive-an-operation)
  • 完整和正确的实现可以在这里找到:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

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

CodePen如何工作的示例是:

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

也可以看看

  • 本地文档Array.prototype.forEach().
  • for_each ... in(MDN)中,它被解释for each (variable in object)为作为ECMA-357(EAX)标准的一部分被弃用.
  • for ...(MDN)描述了迭代使用for (variable of object)作为Harmony(ECMAScript 6)提议的一部分的下一种方式.

有三种实现方式foreach中的jQuery如下.

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
  • 控制台日志仅用于演示。这是使其成为一个完整的运行示例。

从ES6开始:

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

of避免了相关的怪异in,并使其喜欢的工作for任何其他语言的循环,并let结合i作为函数内相对于内环路.

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


可能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
//!!!

for each原生JavaScript中没有任何循环.您可以使用库来获得此功能(我建议使用Underscore.js),使用简单的forin循环.

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

但是,请注意,可能有理由使用更简单的for循环(请参阅堆栈溢出问题为什么在数组迭代中使用"for ... in"这样一个坏主意?)

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

这是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标签变为绿色

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


在JavaScript 中有几种循环数组的方法,如下所示:

因为 - 这是最常见的一个.用于循环的完整代码块

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></p>
<p></p>
<p></p>
<p></p>

while - 循环而条件通过.它似乎是最快的循环

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

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

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

功能循环 - ,forEach,map,filterreduce(他们通过循环功能,但如果你需要做的事情与你的阵列等使用

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

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


使用Arrays的ECMAScript5(Javascript版本).

forEach - 遍历数组中的每个项目,并为每个项目执行所需的任何操作.

['C', 'D', 'E'].forEach(function(element, index) {
console.log(element + " is #" + (index+1) + " in the 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]

希望这会有用.


没有内置的能力进入forEach.要中断执行,请使用Array#some以下内容:

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

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


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

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

优点:

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

缺点:

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


jQuery方式使用$.map:

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

表现

今天(二○一九年十二月一十八日)我对我进行测试的MacOS v10.13.6(高塞拉利昂),在Chrome v 79.0,Safari浏览器v13.0.4和Firefox v71.0(64位) -关于优化结论(和微优化其通常不值得将其引入代码中,因为收益很小,但代码复杂度会增加)。

细节

在测试中,我们计算数组元素的总和。我对小数组(10 个元素)和大数组(1M 个元素)进行了测试,并将它们分为三组:

  • A -for测试
  • B -while测试
  • C - 其他/替代方法
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
//let arr = Array.from(Array(1000000), (x, i) => i%10);
function Aa(a, s=0) {
for(let i=0; i<a.length; i++) {
s += a[i];
}
console.log('Aa=', s);
}
function Ab(a, s=0) {
let n = a.length;
for(let i=0; i<n; i++) {
s += a[i];
}
console.log('Ab=', s);
}
function Ac(a, s=0) {
for(let i=a.length; i--;) {
s += a[i];
}
console.log('Ac=', s);
}
function Ad(a, s=0) {
for(let x of a) {
s += x;
}
console.log('Ad=', s);
}
function Ae(a, s=0) {
for(let i in a) if (a.hasOwnProperty(i)) {
s += a[i];
}
console.log('Ae=', s);
}
function Ba(a, s=0) {
let i = -1;
while(++i < a.length) {
s+= a[i];
}
console.log('Ba=', s);
}
function Bb(a, s=0) {
let i = -1;
let n = a.length;
while(++i < n) {
s+= a[i];
}
console.log('Bb=', s);
}
function Bc(a, s=0) {
let i = a.length;
while(i--) {
s += a[i];
}
console.log('Bc=', s);
}
function Bd(a, s=0) {
let i = 0;
do {
s+= a[i]
} while (++i < a.length);
console.log('Bd=', s);
}
function Be(a, s=0) {
let i = 0;
let n = a.length;
do {
s += a[i]
} while (++i < n);
console.log('Be=', s);
}
function Bf(a, s=0) {
const it = a.values();
let e;
while (!(e = it.next()).done) {
s+= e.value;
}
console.log('Bf=', s);
}
function Ca(a, s=0) {
a.map(x => { s+=x });
console.log('Ca=', s);
}
function Cb(a, s=0) {
a.forEach(x => { s+=x });
console.log('Cb=', s);
}
function Cc(a, s=0) {
a.every(x => (s += x, 1));
console.log('Cc=', s);
}
function Cd(a, s=0) {
a.filter(x => { s+=x });
console.log('Cd=',s);
}
function Ce(a, s=0) {
a.reduce((z, c) => { s+=c }, 0);
console.log('Ce=', s);
}
function Cf(a, s=0) {
a.reduceRight((z, c) => { s += c }, 0);
console.log('Cf=', s);
}
function Cg(a, s=0) {
a.some(x => { s += x } );
console.log('Cg=', s);
}
function Ch(a, s=0) {
Array.from(a, x=> s += x);
console.log('Cc=', s);
}
Aa(arr);
Ab(arr);
Ac(arr);
Ad(arr);
Ae(arr);
Ba(arr);
Bb(arr);
Bc(arr);
Bd(arr);
Be(arr);
Bf(arr);
Ca(arr);
Cb(arr);
Cc(arr);
Cd(arr);
Ce(arr);
Cf(arr);
Cg(arr);
Ch(arr);
<p>This snippets only PRESENTS code used for benchmark - it not perform test itself</p>

跨浏览器结果

所有测试浏览器的结果

浏览器**

具有 10 个元素的数组

铬的结果。您可以在此处在您的机器上执行测试。

具有 1,000,000 个元素的数组

铬的结果。您可以在此处在您的机器上执行测试


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

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

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

示例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:用a key和.循环value

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

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

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

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

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

你可以像这样调用 forEach:

forEach将迭代您提供的数组,并且对于每次迭代,它都会element保存该迭代的值。如果您需要索引,您可以通过在iforEach 的回调函数中将 the 作为第二个参数传递来获取当前索引。

Foreach 基本上是一个高阶函数,它以另一个函数为参数。

let theArray= [1,3,2];
theArray.forEach((element) => {
// Use the element of the array
console.log(element)
}

输出:

1
3
2

您还可以像这样迭代数组:

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

概括:

在迭代数组时,我们通常希望实现以下目标之一:

  1. Array.prototype.map

  2. Array.prototype.forEach

    for..of 环形

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

创建新数组: Map

map()是一个函数,Array.prototype它可以转换数组的每个元素,然后返回一个数组。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返回一个新数组并保持旧数组完好无损(除非您使用 think like 显式操作它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);
}

根据 ECMAScript 6 (ES6) 和 ECMAScript 2015 的新更新功能,您可以将以下选项与循环一起使用:

for(var i = 0; i < 5; i++){
console.log(i);
}
// Output: 0,1,2,3,4
let obj = {"a":1, "b":2}
for(let k in obj){
console.log(k)
}
// Output: a,b
let array = [1,2,3,4]
array.forEach((x) => {
console.log(x);
})
// Output: 1,2,3,4
let array = [1,2,3,4]
for(let x of array){
console.log(x);
}
// Output: 1,2,3,4
let x = 0
while(x < 5){
console.log(x)
x++
}
// Output: 1,2,3,4
let x = 0
do{
console.log(x)
x++
}while(x < 5)
// Output: 1,2,3,4

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

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

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

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

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

如果你有一个庞大的阵列,你应该使用它iterators来获得一些效率。迭代器是某些 JavaScript 集合(如MapSetStringArray)的属性。甚至,在引擎盖下for..of使用iterator

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

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

使用如下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}

注意

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

您可以iteration protocol 在此处阅读更多信息。


如果要使用箭头功能遍历对象数组:

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

您可以使用:

  1. theArray.forEach(function (array, index) {
    console.log(index);
    console.log(array);
    });
    
  2. for(var i=0; i<theArray.length; i++) {
    console.log(i)
    }
    
  3. theArray.map(x => console.log(x));
    
  4. theArray.filter(x => console.log(x));
    

还有很多其他的迭代。


以上是For-each在JavaScript中的数组?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>