在JavaScript中循环一个数组

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

5107483 次浏览

太长别读

但是还有很多更多的东西要探索,继续阅读…


JavaScript在循环数组和类似数组的对象方面具有强大的语义学。我将答案分为两部分:真正数组的选项,以及仅数组喜欢的选项,例如arguments对象、其他可迭代对象(ES2015+)、DOM集合等。

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

对于实际数组

您有五个选项(两个基本上永远支持,另一个由ECMAScript 5[“ES5”]添加,还有两个在ECMAScript 2015(“ES2015”,又名“ES6”)中添加:

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

(你可以在这里看到那些旧的规格:ES5ES2015,但两者都被取代了;当前编辑器的草稿总是这里

详情:

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

ES2015在JavaScript中添加了迭代器和迭代器。数组是可迭代的(字符串、MapSet也是如此,DOM集合和列表也是如此,你将在后面看到)。可迭代对象为它们的值提供迭代器。新的for-of语句循环遍历迭代器返回的值:

const a = ["a", "b", "c"];for (const element of a) { // You can use `let` instead of `const` if you likeconsole.log(element);}// a// b// c

没有比这更简单的了!在幕后,它从数组中获取一个迭代器,并循环遍历迭代器返回的值。数组提供的迭代器提供数组元素的值,从开始到结束。

请注意element是如何限定到每次循环迭代的;在循环结束后尝试使用element将失败,因为它不存在于循环主体之外。

理论上,for-of循环涉及多个函数调用(一个用于获取迭代器,然后一个用于从中获取每个值)。即使这是真的,也没什么好担心的,函数调用在现代JavaScript引擎中是<强>非常便宜(在我研究它之前,它困扰了我forEach[下面];细节)。但此外,JavaScript引擎在处理数组等本机迭代器时优化了这些调用(在性能关键代码中)。

for-of完全对async友好。如果你需要循环主体中的工作串联(而不是并行)完成,循环主体中的await将等待Promise解决后再继续。这是一个愚蠢的例子:

function delay(ms) {return new Promise(resolve => {setTimeout(resolve, ms);});}
async function showSlowly(messages) {for (const message of messages) {await delay(400);console.log(message);}}
showSlowly(["So", "long", "and", "thanks", "for", "all", "the", "fish!"]);// `.catch` omitted because we know it never rejects

请注意单词在每个单词之前出现延迟的方式。

这是一个编码风格的问题,但是for-of是我在循环任何可迭代的东西时首先达到的。

2.使用forEach和相关

在任何即使是模糊的现代环境(所以不是IE8)中,您可以访问ES5添加的Array功能,如果您只处理同步代码(或者您不需要等待异步进程在循环期间完成),则可以使用forEach规格|MDN):

const a = ["a", "b", "c"];a.forEach((element) => {console.log(element);});

forEach接受一个回调函数,并且可以选择在调用该回调时用作this的值(上面没有使用)。回调为数组中的每个元素调用,依次跳过稀疏数组中不存在的元素。虽然我在上面只使用了一个参数,但回调是用三个参数调用的:该迭代的元素、该元素的索引以及对您正在迭代的数组的引用(如果您的函数还没有它)。

for-of一样,forEach的优点是您不必在包含范围内声明索引和值变量;在这种情况下,它们作为参数提供给迭代函数,并且很好地限定了该迭代的范围。

for-of不同,forEach的缺点是它不理解async函数和await。如果你使用async函数作为回调,forEach会在继续之前等待该函数的承诺解决。这是for-of使用forEachasync示例-注意初始延迟,但随后所有文本立即出现而不是等待:

function delay(ms) {return new Promise(resolve => {setTimeout(resolve, ms);});}
async function showSlowly(messages) {// INCORRECT, doesn't wait before continuing,// doesn't handle promise rejectionsmessages.forEach(async message => {await delay(400);console.log(message);});}
showSlowly(["So", "long", "and", "thanks", "for", "all", "the", "fish!"]);// `.catch` omitted because we know it never rejects

forEach是“循环遍历它们”函数,但ES5定义了其他几个有用的“通过数组工作并做事”函数,包括:

  • every规格|MDN)-回调第一次返回假值时停止循环
  • some规格|MDN)-在回调第一次返回truthy值时停止循环
  • filter规格|MDN)-创建一个新数组,其中包含回调返回真值的元素,省略不返回真值的元素
  • map规格|MDN)-从回调返回的值创建一个新数组
  • reduce规格|MDN)-通过重复调用回调来构建一个值,传入以前的值;有关详细信息,请参阅规范
  • reduceRight规格|MDN)-类似于reduce,但以降序而不是升序工作

forEach一样,如果您使用async函数作为回调,这些函数都不会等待函数的Promise解决。这意味着:

  • 使用async函数回调永远不适合everysomefilter,因为它们会将返回的Promise视为truthy值;他们不要等待Promise结算,然后使用实现值。
  • 使用async函数回调通常适用于map如果,目标是将某个数组转换为承诺的数组,也许是为了传递给一个Promise组合函数(#2#3map0或map1)。
  • 使用async函数回调很少适合reducereduceRight,因为(再次)回调将始终返回一个Promise。但是有一种习惯用法是从使用reduceconst promise = array.reduce((p, element) => p.then(/*...something using `element`...*/));)的数组构建一系列Promise,但通常在这些情况下,async函数中的for-offor循环会更清晰,更容易调试。

3.使用简单的for循环

有时旧方法是最好的:

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

如果数组的长度在循环过程中不会改变,并且它是在对性能高度敏感的代码中,那么一个稍微复杂一点的版本在前面获取长度可能会快<强>微小位:

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

或者倒数:

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

但是使用现代JavaScript引擎,你很少需要勉强维持最后一点果汁。

在ES2015之前,循环变量必须存在于包含范围中,因为var只有函数级范围,而不是块级范围。但是正如你在上面的例子中看到的,你可以在for中使用let来将变量的范围仅限于循环。当你这样做时,index变量会为每次循环迭代重新创建,这意味着在循环主体中创建的闭包在该特定迭代中保留对index的引用,这解决了旧的“循环中的闭包”问题:

// (The `NodeList` from `querySelectorAll` is array-like)const 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>

在上面,如果您单击第一个,您会得到“Index is: 0”,如果您单击最后一个,您会得到“Index is: 4”。如果您使用var而不是let,这会使没有起作用(您总是会看到“Index is: 5”)。

for-of一样,for循环在async函数中运行良好。这是使用for循环的早期示例:

function delay(ms) {return new Promise(resolve => {setTimeout(resolve, ms);});}
async function showSlowly(messages) {for (let i = 0; i < messages.length; ++i) {const message = messages[i];await delay(400);console.log(message);}}
showSlowly(["So", "long", "and", "thanks", "for", "all", "the", "fish!"]);// `.catch` omitted because we know it never rejects

4.使用for-in正确

for-in不是用于循环遍历数组,而是用于循环遍历对象属性的名称。作为数组是对象这一事实的副产品,它似乎确实适用于循环遍历数组,但它不仅仅是循环遍历数组索引,它还循环遍历对象的所有可枚举属性(包括继承的属性)。(过去也没有指定顺序;现在是[详细信息在另一个答案中],但即使现在指定了顺序,规则也很复杂,也有例外,依赖顺序不是最佳实践。)

数组上for-in的唯一真正用例是:

  • 这是一个稀疏数组,其中有大规模个间隙,或者
  • 您在数组对象上使用非元素属性,并且希望将它们包含在循环中

只看第一个例子:如果您使用适当的保护措施,您可以使用for-in访问这些稀疏数组元素:

// `a` is a sparse arrayconst a = [];a[0] = "a";a[10] = "b";a[10000] = "c";for (const name in a) {if (Object.hasOwn(a, name) &&       // These checks are/^0$|^[1-9]\d*$/.test(name) &&  // explainedname <= 4294967294              // below) {const element = a[name];console.log(a[name]);}}

注意三个检查:

  1. 该对象具有该名称的自己属性(不是它从其原型继承的属性;此检查也通常写为a.hasOwnProperty(name),但ES2022添加了#1,这可能更可靠),并且

  2. 名称都是十进制数字(例如,普通字符串形式,而不是科学记数法),并且

  3. 当强制转换为数字时,名称的值是<=2^32-2(即4,294,967,294)。这个数字是从哪里来的?它是数组索引在规范定义的一部分。其他数字(非整数、负数、大于2^32-2的数字)不是数组索引。它是2^32-2的原因是使得最大索引值低于2^32-1,这是数组length可以具有的最大值。(例如,数组的长度适合32位无符号整数。)

…尽管如此,大多数代码只做hasOwnProperty检查。

当然,您不会在内联代码中这样做。您可以编写一个实用函数。也许:

// Utility function for antiquated environments without `forEach`const hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);const rexNum = /^0$|^[1-9]\d*$/;function sparseEach(array, callback, thisArg) {for (const name in array) {const index = +name;if (hasOwn(a, name) &&rexNum.test(name) &&index <= 4294967294) {callback.call(thisArg, array[name], index, array);}}}
const a = [];a[5] = "five";a[10] = "ten";a[100000] = "one hundred thousand";a.b = "bee";
sparseEach(a, (value, index) => {console.log("Value at " + index + " is " + value);});

for一样,如果需要串联完成异步函数中的工作,则for-in在异步函数中运行良好。

function delay(ms) {return new Promise(resolve => {setTimeout(resolve, ms);});}
async function showSlowly(messages) {for (const name in messages) {if (messages.hasOwnProperty(name)) { // Almost always this is the only check people doconst message = messages[name];await delay(400);console.log(message);}}}
showSlowly(["So", "long", "and", "thanks", "for", "all", "the", "fish!"]);// `.catch` omitted because we know it never rejects

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

for-of隐式使用迭代器,为你做所有的短接工作。有时,你可能想使用迭代器明确。它看起来像这样:

const a = ["a", "b", "c"];const it = a.values(); // Or `const it = a[Symbol.iterator]();` if you likelet entry;while (!(entry = it.next()).done) {const element = entry.value;console.log(element);}

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

value的结果因迭代器而异。在数组上,默认迭代器提供每个数组元素的值(前面示例中的"a""b""c")。数组还有其他三个返回迭代器的方法:

  • values():这是返回默认迭代器的[Symbol.iterator]方法的别名。
  • keys():返回一个提供数组中每个键(索引)的迭代器。在上面的示例中,它将提供"0",然后"1",然后"2"(是的,作为字符串)。
  • entries():返回一个提供[key, value]数组的迭代器。

由于迭代器对象在调用next之前不会前进,因此它们在async函数循环中运行良好。这是前面的for-of示例显式使用迭代器:

function delay(ms) {return new Promise(resolve => {setTimeout(resolve, ms);});}
async function showSlowly(messages) {const it = messages.values()while (!(entry = it.next()).done) {await delay(400);const element = entry.value;console.log(element);}}
showSlowly(["So", "long", "and", "thanks", "for", "all", "the", "fish!"]);// `.catch` omitted because we know it never rejects

对于类数组对象

除了true数组之外,还有类数组对象具有length属性和全数字名称的属性:#1实例#2实例arguments对象等。

使用上面的大多数选项

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

  1. 使用#0(隐式使用迭代器)(ES2015+)

    for-of使用对象提供的HTMLCollection3(如果有)。这包括主机提供的对象(如DOM集合和列表)。例如,getElementsByXYZ方法的HTMLCollection实例和querySelectorAll方法的NodeLists实例都支持迭代。(这是由超文本标记语言和DOM规范巧妙地定义的HTMLCollection4。基本上,任何具有length和索引访问权限的对象都是自动可迭代的。它HTMLCollection5必须被标记为iterable;这仅用于除了可迭代之外还支持forEachvalueskeysHTMLCollection0方法的集合。NodeList支持;HTMLCollection不支持,但两者都是可迭代的。)

    下面是循环遍历div元素的示例:

const divs = document.querySelectorAll("div");for (const div of divs) {div.textContent = Math.random();}
<div>zero</div><div>one</div><div>two</div><div>three</div><div>four</div>

  1. 使用#0及相关(ES5+)

    Array.prototype上的各种函数是“有意通用的”,可以通过Function#call规格|MDN)或Function#apply规格|MDN)在类似数组的对象上使用。(如果你必须处理IE8或更早版本[哎哟],请参阅本答案末尾的“主机提供对象的警告”,但这不是模糊现代浏览器的问题。)

    假设您想在NodechildNodes集合上使用forEach(作为HTMLCollection,本机没有forEach)。你会这样做:

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

    (注意,你可以在node.childNodes上使用for-of

    如果您要经常这样做,您可能希望将函数引用的副本获取到变量中以供重用,例如:

    // (This is all presumably in a module or some scoping function)const forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
    // Then later...forEach(node.childNodes, (child) => {// Do something with `child`});
  2. 使用简单的#0循环

    也许很明显,一个简单的for循环适用于类似数组的对象。

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

    见#1。

<强>可能能够逃脱for-in(有保障措施),但有了所有这些更合适的选择,没有理由去尝试。

创建一个真正的数组

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

  1. 使用#0

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

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

    …从querySelectorAll中获取NodeList并从中创建一个数组。

    如果您要以某种方式映射内容,映射函数很方便。例如,如果您想获取具有给定类的元素的标记名称的数组:

    // Typical use (with an arrow function):const divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
    // Traditional function (since `Array.from` can be polyfilled):var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {return element.tagName;});
  2. 使用扩展语法(#0)

    也可以使用ES2015的扩展语法。与for-of一样,这使用对象提供的迭代器(参见上一节的扩展语法):

    const trueArray = [...iterableObject];

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

    const divs = [...document.querySelectorAll("div")];
  3. 使用数组的#0方法

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

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

    例如,如果我们想将NodeList转换为true数组,我们可以这样做:

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

    (如果你仍然必须处理IE8[哎哟],将失败;IE8不允许你像那样使用主机提供的对象this。)

主机提供的对象的警告

如果你使用Array.prototype函数和主机提供类数组对象(例如,DOM集合等由浏览器而不是JavaScript引擎提供),IE8等过时的浏览器不一定会以这种方式处理,所以如果你必须支持它们,一定要在你的目标环境中测试。但这对模糊现代的浏览器来说不是问题。(对于非浏览器环境,自然会取决于环境。)

说明:这个答案已经过时了。要获得更现代的方法,请查看数组中可用的方法。感兴趣的方法可能是:

  • for每个
  • 地图
  • 过滤器
  • zip
  • 减少
  • 一些

javascript中迭代数组的标准方法是vanillafor-loop:

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

然而,请注意,这种方法只有在你有一个密集数组,并且每个索引都被一个元素占用时才有效。如果数组是稀疏的,那么你可能会遇到这种方法的性能问题,因为你将迭代很多数组中不存在真的的索引。在这种情况下,for .. in-loop可能是一个更好的主意。

ECMAScript 5中,数组原型上会有一个for每个方法,但它在传统浏览器中不受支持。因此,为了能够始终如一地使用它,你必须有一个支持它的环境(例如,服务器端JavaScript的Node.js),或者使用“Poly填充”。然而,用于此功能的Poly填充很琐碎,因为它使代码更易于阅读,所以它是一个很好的Poly填充。

如果您想在数组上循环,请使用标准的三部分for循环。

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

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

本地javascript中没有任何for each循环。您可以使用库来获取此功能(我推荐Underscore.js),使用简单的for in循环。

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

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

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

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

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

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

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

此外,ECMAScript 5Array.prototype中添加了#0方法,该方法可用于使用回调枚举数组(在文档中提供了poly填充,因此您仍然可以将其用于较旧的浏览器):

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

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

如果您使用的是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].}

如果你不介意清空数组:

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

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

for每个实现(在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){...});

另见

  • 原生文档#0.
  • for_each(MDN)中,解释了for each (variable in object)作为ECMA-357(EAX)标准的一部分已被弃用。
  • 为…的(MDN)描述了使用for (variable of object)作为Harmony(ECMAScript 6)提案的一部分的下一种迭代方式。

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

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

这是一个非稀疏列表的迭代器,索引从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标签变成了绿色

使用字符串作为函数时要小心:该函数是在断章取义的情况下创建的,应该仅在您确定变量范围的情况下使用。否则,最好在范围更直观的地方传递函数。

使用$.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];

反向循环

我认为 倒车 for 循环值得一提:

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

优点:

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

缺点:

  • 它以相反的顺序处理这些项目。如果您正在根据结果构建一个新的数组,或者在屏幕上打印东西,自然就会按照原始顺序使用 输出将被逆转
  • 作为第一个子元素,重复地将兄弟元素插入 DOM 以保持它们的顺序是 没那么有效率。(浏览器将不得不不断调整。)为了有效和有序地创建 DOM 节点,只需要像往常一样进行循环和附加(还要使用“文档片段”)。
  • 对初级开发人员而言,反向循环是 很困惑(根据您的观点,您可能认为这是一个优势)

我应该一直用吗?

有些开发人员对循环 默认情况下使用相反的方法,除非有很好的理由进行循环。

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

“只要对清单上的每一项都这样做,我不在乎订单!”

然而,在实践中,这是 没有实际上是一个可靠的意图指示,因为它是无法区分的情况下,当您 关心的顺序,并真正做 需要反向循环。因此,实际上需要另一个构造来准确表达“不在乎”的意图,这种意图目前在大多数语言中是不可用的,包括 ECMAScript,但可以称为 forEachUnordered()

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

最好使用 for each ()

一般来说,对于 清晰度和安全性比较重要的高级代码,我以前建议使用 Array::forEach作为循环的默认模式(尽管现在我更喜欢使用 for..of)。选择 forEach而不是反向循环的理由如下:

  • 读起来更清楚。
  • 它表明 不会在块内移位(这总是一个可能的意外隐藏在长的 forwhile循环中)。
  • 它为闭包提供了一个自由作用域。
  • 它减少了局部变量的泄漏和与外部变量的偶然碰撞(和突变)。

然后,当您在代码中看到反向 for 循环时,这暗示它是出于好的原因(可能是上面描述的原因之一)而反向的。看到一个传统的前进循环可能表明转移可以发生。

(如果对意图的讨论对您没有任何意义,那么您和您的代码可以从观看克罗克福德关于 编程风格与大脑的讲座中获益。)

现在用来... 的效果更好了!

关于 for..of还是 forEach()更可取,目前存在争议:

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

  • 基于这个原因(并鼓励使用 mapfilter) ,一些前端风格指南完全禁止 for..of

  • 但是上述担忧不适用于 Node.js 应用程序,因为它们现在很好地支持 for..of

  • 此外,forEach()内部的 await 不起作用。在这种情况下,使用 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上的项目,这样就避免了 数组出界 undefined项目的任何问题。

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

    当条件 i--的计算结果为一个假值(当结果为0时) ,循环将停止迭代。

    诀窍在于,与 --i不同,后面的 i--操作符递减 i,但是产生值 递减。您的控制台可以演示这一点:

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

    [5, 5, 4]

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

    在传统的转发 for loop 中,i++++i是可互换的(正如道格拉斯·克罗克福特所指出的)。但是在 for 循环的相反情况下,由于递减也是条件表达式,因此如果要处理索引为0的项,则必须坚持使用 i--


冷知识

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

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

感谢 WYL 向我展示了反向 for 循环的优点和缺点。

我知道这是一篇老文章,而且已经有很多很棒的回答了。为了更加完整,我想我可以使用 AngularJS再添加一个。当然,这只适用于使用 Angular 的情况,尽管如此,我还是想说。

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

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

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将使用 ES5forEvery 循环。现在,我将在 con 部分讨论效率问题,因为 forEach 循环比 for 循环慢 很多。我提到这个是作为一个专业人士,因为这是很好的一致性和标准化。

考虑下面的两个嵌套循环,它们做完全相同的事情。假设我们有两个对象数组,每个对象包含一个结果数组,每个结果数组都有一个 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实际上是由“ 意外”支持的,要在 angular.forEach中继续,你只需在函数(如 angular.forEach(array, function(item) { if (someConditionIsTrue) return; });)中放入一个 return;语句,这将导致它在该迭代中继续离开函数。这也是由于本机 forEach不支持中断或继续。

我肯定还有其他各种各样的优缺点,请随意添加任何你认为合适的。我觉得,底线,如果你需要效率,坚持只有本地 for循环为您的循环需要。但是,如果您的数据集较小,并且为了可读性和可写性而放弃一些效率是可以接受的,那么无论如何都要在这个坏家伙中投入一个 angular.forEach

forEach中没有内置的中断能力。要中断执行,可以使用如下 Array#some:

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

这是因为 some只要以数组顺序执行的任何回调函数返回 true,就会返回 true,从而缩短了其余回调函数的执行。 原始答案 参见 < a href = “ http://www.ecma-international. org/ecma-262/5.1/# sec-15.4.4.17”rel = “ noReferrer”> some

我还想添加这个作为一个反向循环的组合和一个答案上面的人,希望这种语法太。

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

优点:

这样做的好处是: 您已经在第一行中有了引用,因此不需要在以后用另一行来声明。在循环访问对象数组时非常方便。

缺点:

当引用为 false-falsey (未定义等)时,这将中断。不过这也可以作为一种优势。然而,这会使它更难以阅读。而且根据不同的浏览器,它可以“不”优化工作速度比原来的一个。

从 ECMAScript 6开始:

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

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

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

ECMAScript 5(基于 JavaScript 的版本)与 Array 协同工作:

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 -顾名思义,它通过调用当前元素中传递的给定函数和前一次执行的结果,将数组减少为一个值。

[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 -非常类似于过滤器返回一个数组,其中的元素返回给定函数的真值。

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


// Output: [2,4,6]

有几种方法循环通过一个 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 id="example"></p>

while - loop while a condition is through. It seems to be the fastest loop

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

do/while - also loop through a block of code while the condition is true, will run at least one time

var text = ""
var i = 0;


do {
text += i + ") something <br>";
i++;
}
while (i < 10);


document.getElementById("example").innerHTML = text;
<p id="example"></p>

Functional loops - forEach, map, filter, also reduce (they loop through the function, but they are used if you need to do something with your array, etc.

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

For more information and examples about functional programming on arrays, look at the blog post Functional programming in JavaScript: map, filter and reduce.

与您的想法最接近的一种方法是使用 Array.forEach(),它接受一个闭包函数,该函数将对数组的每个元素执行。

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

另一种可行的方法是使用 Array.map(),它的工作原理是相同的,但是它也接受你返回的所有值,并将它们返回到一个新的数组中(实际上是将每个元素映射到一个新的元素) ,像这样:

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


console.log(myArray); // [2, 3, 4]

Lambda 语法通常不适用于 Internet Explorer 10或以下版本。

我通常用

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

如果您是一个 jQuery风扇并且已经运行了一个 jQuery 文件,那么您应该反转索引和值参数的位置

$("#ul>li").each(function(**index, value**){
console.log("value of the looped element" + value);
console.log("index of the looped element" + index);
});
var a = ["car", "bus", "truck"]
a.forEach(function(item, index) {
console.log("Index" + index);
console.log("Element" + item);
})

如果您想使用 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 集合(如 MapSetMap0、 Map1)的属性。甚至,Map2在引擎盖下使用 Map3。

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

通过调用迭代器的 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}

注意 :

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

你可以阅读更多关于 iteration protocol 给你的资料。

你可以这样呼叫:

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

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返回一个新数组,并保留旧数组的完整性(除非您使用类似于 splice的思想显式地操作它)。

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

使用 forEach循环

forEach是一个位于 Array.prototype上的函数,它接受一个回调函数作为参数。然后对数组中的每个元素执行这个回调函数。与 map()函数不同,forEvery 函数不返回任何值(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 解体扩散算符扩散算符的循环

对于 ECMAScript 6的新手来说,解构和使用扩展操作符被证明是非常有用的,因为它更具人类可读性/美观性,尽管一些 JavaScript 老手可能会认为它很麻烦。大三学生或其他人可能会觉得有用。

下面的示例将使用 for...of语句和 .forEach方法。

示例6、7和8 可用于任何功能环,如 .map.filter.reduce.sort.every.some。有关这些方法的更多信息,请查看 数组对象

示例1: Normal for...of loop-这里没有技巧。

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: .forEach一起使用 例子4

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 是数组,instance是数组的元素:

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

但最终,两者都在做同样的事情。

// Looping through arrays using the foreach ECMAScript 6 way


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

如果希望以函数方式保存代码,请使用 map:

theArray.map(instance => do_something);

这样,您将生成一个新的数组,以便将来操作,并将跳过任何不希望的副作用。

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

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

Mozilla 文档

/* Get all forms */
document.querySelectorAll( "form" ).forEach( form => {


/* For each form, add the onsubmit event */
form.addEventListener( "submit", event => {
event.preventDefault(); // Return false


/* Display it */
alert(event.target.action);
console.log(event.target);
} );


} );
<form action="form1.php" >
<input type="submit" value="Submit" />
</form>
<form action="form2.php" >
<input type="submit" value="Submit" />
</form>
<form action="form3.php" >
<input type="submit" value="Submit" />
</form>

表演

今天(2019-12-18)我在我的 MacOS v10.13.6(High Sierra)、 Chrome v79.0、 Safari v13.0.4和 Firefox v71.0(64位)上进行了测试——关于优化的结论(以及 微观优化,它通常不值得引入代码,因为它的好处很小,但代码复杂度增加了)。

  • 看起来传统的 for i()是在所有浏览器上编写快速代码的一个很好的选择。

  • 其他的解决方案,比如 for-of(广告) ,都在 C.组中... 通常比 慢2-10倍(甚至更多) ,但是对于小数组来说,为了增加代码的清晰度,可以使用它。

  • 数组长度缓存在 n(爸爸,爸爸,爸爸)中的循环有时会更快,有时不会。编译器可能会自动检测这种情况并引入缓存。缓存版本和非缓存版本(啊,巴,巴)之间的速度差异约为1% ,所以看起来引入的 n微观优化

  • 循环从最后一个数组元素(Ac 和 BC)开始的类似 i--的解决方案通常比正向解决方案慢30% ——可能是因为 CPU 内存缓存正常的方式——正向内存读取对于 CPU 缓存来说更为优化)。建议不要使用此类解决方案。

细节

在测试中,我们计算数组元素的和。我对小数组(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 style="color: red">This snippets only PRESENTS code used for benchmark - it not perform test itself</p>

Cross browser results

Results for all tested browsers

Enter image description herebrowsers**

Array with 10 elements

Results for Chrome. You can perform the test on your machine here.

Enter image description here

Array with 1,000,000 elements

Results for Chrome. You can perform the test on your machine here

Enter image description here

你可使用:

  1. 每个人

    theArray.forEach(function (array, index) {
    console.log(index);
    console.log(array);
    });
    
  2. for

    for(var i=0; i<theArray.length; i++) {
    console.log(i)
    }
    
  3. map

    theArray.map(x => console.log(x));
    
  4. map

    theArray.filter(x => console.log(x));
    

And there are many others for iteration.

我认为 for/of是正确的选择:

const arr = ['a', 'b', 'c'];


for (const v of arr) {
console.log(v); // Prints "a", "b", "c"
}

  • for/in不同,for/of跳过数组上的非数值属性。例如,如果设置 arr.foo = 'test'for (var v in arr)将循环通过 'foo'键。

  • forEach()不同,for/of不会跳过数组中的“漏洞”。const arr = ['a',, 'c']是有效的 JavaScript,只是第二个元素是一个“洞”。该数组在功能上等效于 ['a', undefined, 'c']

你可以在 这篇关于 ABC0和 forEach()的博客文章。阅读更多

根据新更新的特性 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

While 循环

let x = 0


while(x < 5){
console.log(x)
x++
}


// Output: 1,2,3,4

Do... while 循环

let x = 0


do{
console.log(x)
x++
}while(x < 5)


// Output: 1,2,3,4

假设我们有一个主题数组:

let ddl = new Array();
if (subjects) {
subjects.forEach(function (s) {ddl.push({"id": s.id, "label": s.name});});
}

每张地图

使用现代 JavaScript 语法迭代数组

const fruits = ['🍎', '🍋', '🍌' ]

为了... 为了..

for (const fruit of fruits) {
console.log(fruit)  // '🍎', '🍋', '🍌'
}

为每个人

fruits.forEach(fruit => {
console.log(fruit)  // '🍎', '🍋', '🍌'
})

地图

* 与上面两个不同的是,map () 创造是一个新的数组,并且期望在每次迭代之后使用 返回

fruits.map(fruit => fruit)   // ['🍎', '🍋', '🍌' ]

重要: 由于 地图()意味着在每次迭代时返回一个值,因此它是转换数组中元素的理想方法:

fruits.map(fruit => 'cool ' + fruit)   // ['cool 🍎', 'cool 🍋', 'cool 🍌' ]

另一方面,为了... 为了..为每个()不需要返回任何东西,这就是为什么我们通常使用它们来执行逻辑任务,操作外部的东西。

因此,我们可以在这两个例子中找到 if ()语句、副作用和日志记录活动。

提示: 您还可以在每次迭代中使用索引(以及整个数组)。地图()或。For each ()函数。

只要向他们传递额外的论点:

fruits.map((fruit, i) =>  i + '  ' + fruit)


// ['0 🍎', '1 🍋', '2 🍌' ]


fruits.forEach((f, i, arr) => {
console.log( f + ' ' + i + ' ' +  arr )
})


// 🍎  0  🍎, 🍋, 🍌,
// 🍋  1  🍎, 🍋, 🍌,
// 🍌  2  🍎, 🍋, 🍌,

尽可能使用 for...of

Table

正如我们在上面的图片中看到的那样,for...of应该在它适合的地方使用。由于它支持异步函数,因此可以跳过非数值属性,并防止意外修改循环索引而扰乱循环。

语法

const nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const num of nums) {
/* Do something with num */
}

for...of 参考文献的更多例子,链接到规格和 for...offor...in之间的区别。

另外,请参阅图像的 教程和源代码