如何使用 JavaScript 遍历数组中的所有条目?
TL;博士
您最好的选择通常是 for-of 循环(仅限 ES2015+;规范 | MDN)- 简单且异步友好 for (const element of theArray) { // ...use `element`... } forEach(仅限 ES5+; spec | MDN)(或它的亲戚) - 不是异步友好的(但请参阅详细信息) theArray.forEach(element => { // ...use `element`... });一个简单的老式 for 循环 - 异步友好 for (let index = 0; index < theArray.length; ++index) { const element = theArray[index]; // ... 使用 `element`... } (很少) for-in 和安全措施 - 异步友好 for (const propertyName in theArray) { if (/*...is an array element property (见下文)。 ..*/) { 常量元素 = theArray[propertyName]; // ...使用`元素`... } }
for-of 循环(仅限 ES2015+;规范 | MDN)- 简单且异步友好的 for (const element of theArray) { // ...使用 `element`... }
forEach(仅限 ES5+;规范 | MDN)(或其亲属等) - 不是异步友好的(但请参阅详细信息) theArray.forEach(element => { // ...use `element`... });
一个简单的老式 for 循环 - 异步友好 for (let index = 0; index < theArray.length; ++index) { const element = theArray[index]; // ...使用`元素`... }
(很少)带有保护措施的 for-in - 异步友好 for (const propertyName in theArray) { if (/*...is an array element property (see below)...*/) { const element = theArray[propertyName] ; // ...使用`元素`... } }
一些快速的“不要”:不要使用 for-in,除非你使用它有安全措施,或者至少知道它为什么会咬你。如果您不使用它的返回值,请不要使用 map。 (可悲的是,有人教 map [spec / MDN],就好像它是 forEach 一样——但正如我在我的博客上写的那样,这不是它的用途。如果你不使用它创建的数组,请不要使用 map。 ) 如果回调执行异步工作并且您希望 forEach 等到该工作完成(因为它不会),请不要使用 forEach。
不要使用 for-in ,除非你使用它有安全措施,或者至少知道它为什么会咬你。
如果您不使用它的返回值,请不要使用 map。 (可悲的是,有人在教 map [spec / MDN],就好像它是 forEach 一样——但正如我在博客上写的那样,这不是它的用途。如果你不使用它创建的数组,请不要使用 map。 )
如果回调执行异步工作并且您希望 forEach 等到该工作完成(因为它不会),请不要使用 forEach。
但是还有更多需要探索,请继续阅读......
JavaScript 具有强大的语义,用于循环遍历数组和类似数组的对象。我将答案分为两部分:真正数组的选项,以及只是数组的选项 -like,例如 arguments
对象、其他可迭代对象 (ES2015+)、DOM 集合, 等等。
好的,让我们看看我们的选择:
对于实际数组
您有五个选项(两个基本上永远支持,另一个由 ECMAScript 5 ["ES5"] 添加,另外两个在 ECMAScript 2015 中添加("ES2015",又名"ES6"):
使用 for-of(隐式使用迭代器)(ES2015+) 使用 forEach 和相关(ES5+) 使用简单的 for 循环 正确使用 for-in 显式使用迭代器(ES2015+)
(您可以在此处查看这些旧规范:ES5、ES2015,但两者都已被取代;当前编辑的草稿始终为 here。)
细节:
1.使用for-of(隐式使用迭代器)(ES2015+)
ES2015 将 iterators and iterables 添加到 JavaScript。数组是可迭代的(字符串、Map
和 Set
以及 DOM 集合和列表也是如此,稍后您将看到)。可迭代对象为其值提供迭代器。新的 for-of
语句循环遍历迭代器返回的值:
常量 a = ["a", "b", "c"]; for (const element of a) { // 你可以使用 `let` 代替 `const` 如果你喜欢 console.log(element); } // a // b // c
没有比这更简单的了!在幕后,它从数组中获取一个迭代器并循环遍历迭代器返回的值。数组提供的迭代器按开始到结束的顺序提供数组元素的值。
注意 element
是如何作用于每个循环迭代的;在循环结束后尝试使用 element
会失败,因为它不存在于循环体之外。
理论上,for-of
循环涉及多个函数调用(一个用于获取迭代器,然后一个用于从中获取每个值)。即使这是真的,也没什么好担心的,函数调用在现代 JavaScript 引擎中非常很便宜(它困扰了我 forEach
[下文],直到我研究它; details)。但此外,在处理诸如数组之类的本机迭代器时,JavaScript 引擎会优化这些调用(在性能关键代码中)。
for-of
完全对 async
友好。如果您需要以串行方式(而不是并行方式)完成循环体中的工作,则循环体中的 await
将等待承诺解决后再继续。这是一个愚蠢的例子:
function delay(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); } async function showSlowly(messages) { for (const message of messages) { await delay(400);控制台.log(消息); } } showSlowly([ "So", "long", "and", "thanks", "for", "all", "the", "fish!" ]); // `.catch` 被省略,因为我们知道它从不拒绝
请注意单词在每个单词之前出现的延迟。
这是一个编码风格的问题,但是当循环任何可迭代的东西时,for-of
是我首先想到的。
2.使用forEach及相关
在您可以访问 ES5 添加的 Array
功能的任何甚至模糊的现代环境(不是 IE8)中,如果您只处理同步,则可以使用 forEach
(spec | MDN)代码(或者您不需要在循环期间等待异步进程完成):
常量 a = ["a", "b", "c"]; a.forEach((element) => { console.log(element); });
forEach
接受一个回调函数,并且可以选择在调用该回调时用作 this
的值(上面未使用)。为数组中的每个元素调用回调,按顺序跳过稀疏数组中不存在的元素。虽然我在上面只使用了一个参数,但调用回调时使用了三个参数:该迭代的元素、该元素的索引以及对您正在迭代的数组的引用(如果您的函数还没有它)便利)。
与 for-of
一样,forEach
的优点是您不必在包含范围内声明索引和值变量;在这种情况下,它们作为参数提供给迭代函数,并且非常适合该迭代。
与 for-of
不同,forEach
的缺点是它不理解 async
函数和 await
。如果您使用 async
函数作为回调,则 forEach
不会等待该函数的承诺在继续之前完成。这是 for-of
中使用 forEach
的 async
示例 - 请注意初始延迟是如何出现的,但随后所有文本立即出现而不是等待:
function delay(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); } async function showSlowly(messages) { // 错误,在继续之前不等待, // 不处理承诺拒绝 messages.forEach(async message => { await delay(400); console.log(message); } ); } showSlowly([ "So", "long", "and", "thanks", "for", "all", "the", "fish!" ]); // `.catch` 被省略,因为我们知道它从不拒绝
forEach
是“循环遍历它们”函数,但 ES5 定义了其他几个有用的“遍历数组并执行操作”函数,包括:
every (spec | MDN) - 在回调第一次返回虚假值时停止循环
some (spec | MDN) - 在回调第一次返回真值时停止循环
filter (spec | MDN) - 创建一个新数组,包括回调返回真实值的元素,省略不返回真实值的元素
map (spec | MDN) - 从回调返回的值创建一个新数组
reduce (spec | MDN) - 通过重复调用回调建立一个值,传入以前的值;有关详细信息,请参阅规范
reduceRight (spec | MDN) - 类似于 reduce,但按降序而不是升序工作
与 forEach
一样,如果您使用 async
函数作为回调,则这些函数都不会等待函数的承诺解决。这意味着:
对every、some 和filter 使用异步函数回调是不合适的,因为它们会将返回的promise 视为真实值;他们不等待承诺解决,然后使用履行价值。
使用异步函数回调通常适用于 map,如果目标是将数组转换为 promise 数组,可能用于传递给 promise 组合函数之一(Promise.all、Promise.race、promise.allSettled、或 Promise.any)。
在 reduce 或 reduceRight 中使用异步函数回调很少合适,因为(再次)回调将始终返回一个 Promise。但是有一个习惯用法是从一个使用reduce的数组构建一个promise链(const promise = array.reduce((p, element) => p.then(/*...something using `element`...* /));),但通常在这些情况下,异步函数中的 for-of 或 for 循环会更清晰,更易于调试。
3.使用简单的for循环
有时老方法是最好的:
常量 a = ["a", "b", "c"]; for (let index = 0; index < a.length; ++index) { const element = a[index];控制台.log(元素); }
如果数组的长度在循环期间不会改变,并且它在对性能高度敏感的代码中,那么预先获取长度的稍微复杂一点的版本可能会快一点:
常量 a = ["a", "b", "c"]; for (let index = 0, len = a.length; index < len; ++index) { const element = a[index];控制台.log(元素); }
和/或倒数:
常量 a = ["a", "b", "c"]; for (let index = a.length - 1; index >= 0; --index) { const element = a[index];控制台.log(元素); }
但是对于现代 JavaScript 引擎,您很少需要勉强挤出最后一点汁液。
在 ES2015 之前,循环变量必须存在于包含范围内,因为 var
只有函数级范围,没有块级范围。但正如您在上面的示例中看到的,您可以在 for
中使用 let
将变量范围限定为循环。当您这样做时,会为每次循环迭代重新创建 index
变量,这意味着在循环体中创建的闭包会为该特定迭代保留对 index
的引用,这解决了旧的“循环中的闭包”问题:
// (`querySelectorAll` 中的 `NodeList` 类似于数组) const divs = document.querySelectorAll("div"); for (let index = 0; index < divs.length; ++index) { divs[index].addEventListener('click', e => { console.log("Index is: " + index); }); }
在上面,如果单击第一个,则会得到“索引为:0”,如果单击最后一个,则会得到“索引为:4”。如果您使用 var
而不是 let
,这不起作用(您总是会看到“索引为:5”)。
与 for-of
一样,for
循环在 async
函数中运行良好。这是前面使用 for
循环的示例:
function delay(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); } 异步函数 showSlowly(messages) { for (let i = 0; i < messages.length; ++i) { const message = messages[i];等待延迟(400);控制台.log(消息); } } showSlowly([ "So", "long", "and", "thanks", "for", "all", "the", "fish!" ]); // `.catch` 被省略,因为我们知道它从不拒绝
4.正确使用for-in
for-in
不是用于遍历数组,而是用于遍历对象属性的名称。作为数组是对象这一事实的副产品,它似乎经常用于循环遍历数组,但它不仅循环遍历数组索引,它还循环遍历数组的 all 可枚举属性对象(包括继承的对象)。 (以前也是不指定顺序的,现在是【详情见this other answer】,但现在虽然指定了顺序,但规则复杂,也有例外,不依赖顺序最佳实践。)
数组上 for-in
的唯一实际用例是:
它是一个稀疏数组,其中有大量间隙,或者
您在数组对象上使用非元素属性,并且希望将它们包含在循环中
只看第一个示例:如果您使用适当的保护措施,您可以使用 for-in
访问那些稀疏数组元素:
// `a` 是一个稀疏数组 const a = [];一[0] =“一”; a[10] = "b"; a[10000] = "c"; for (const name in a) { if (Object.hasOwn(a, name) && // 这些检查是 /^0$|^[1-9]\d*$/.test(name) && // 解释名称<= 4294967294 // 下面 ) { const element = a[name]; console.log(a[name]); } }
注意三个检查:
对象有它自己的属性(不是从它的原型继承的;这个检查也经常写为 a.hasOwnProperty(name) 但 ES2022 添加了 Object.hasOwn 可以更可靠),并且名称是所有十进制数字(例如,正常的字符串形式,而不是科学记数法),并且当强制转换为数字时,名称的值是 <= 2^32 - 2(即 4,294,967,294)。这个数字是从哪里来的?它是规范中数组索引定义的一部分。其他数字(非整数、负数、大于 2^32 - 2 的数字)不是数组索引。它是 2^32 - 2 的原因是这使得最大索引值比 2^32 - 1 小一,这是数组长度可以具有的最大值。 (例如,数组的长度适合 32 位无符号整数。)
...尽管话虽如此,但大多数代码只进行 hasOwnProperty
检查。
当然,您不会在内联代码中这样做。你会写一个实用函数。也许:
// 没有 `forEach` 的陈旧环境的实用函数 const hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);常量 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); } } } 常量 a = []; a[5] = "五"; a[10] = "十"; a[100000] = "十万"; ab = "蜜蜂"; sparseEach(a, (value, index) => { console.log("" + index + "处的值是" + 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)) { // 几乎总是这是人们做的唯一检查 const message = messages[name];等待延迟(400);控制台.log(消息); } } } showSlowly([ "So", "long", "and", "thanks", "for", "all", "the", "fish!" ]); // `.catch` 被省略,因为我们知道它从不拒绝
5. 显式使用迭代器 (ES2015+)
for-of
隐式使用迭代器,为您完成所有工作。有时,您可能希望显式使用迭代器。它看起来像这样:
常量 a = ["a", "b", "c"]; const it = a.values(); // 或者 `const it = a[Symbol.iterator]();` 如果你喜欢 let entry; while (!(entry = it.next()).done) { const element = entry.value;控制台.log(元素); }
迭代器是与规范中的迭代器定义匹配的对象。每次调用时,它的 next
方法都会返回一个新的 result 对象。结果对象有一个属性 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);常量元素 = entry.value;控制台.log(元素); } } showSlowly([ "So", "long", "and", "thanks", "for", "all", "the", "fish!" ]); // `.catch` 被省略,因为我们知道它从不拒绝
对于类数组对象
除了真正的数组之外,还有 类数组 对象具有 length
属性和全数字名称的属性:NodeList
instances、HTMLCollection
instances、arguments
对象等。我们如何遍历它们的内容?
使用上面的大多数选项
上述数组方法中的至少一些,可能是大多数甚至全部都同样适用于类似数组的对象:
使用 for-of(隐式使用迭代器)(ES2015+)for-of 使用对象提供的迭代器(如果有)。这包括主机提供的对象(如 DOM 集合和列表)。例如,来自 getElementsByXYZ 方法的 HTMLCollection 实例和来自 querySelectorAll 的 NodeLists 实例都支持迭代。 (这是由 HTML 和 DOM 规范非常巧妙地定义的。基本上,任何具有长度和索引访问的对象都是自动可迭代的。它不必标记为可迭代;它仅用于除了可迭代之外的集合,支持 forEach、values、keys 和 entries 方法。NodeList 支持;HTMLCollection 不支持,但两者都是可迭代的。)这是一个遍历 div 元素的示例:
常量 divs = document.querySelectorAll("div"); for (const div of divs) { div.textContent = Math.random(); }
使用 forEach 和相关 (ES5+) Array.prototype 上的各种函数是“有意通用的”,可以通过 Function#call (spec | MDN) 或 Function#apply (spec | MDN) 用于类似数组的对象。 (如果您必须处理 IE8 或更早版本 [哎哟],请参阅此答案末尾的“主机提供的对象的警告”,但这不是模糊现代浏览器的问题。)假设您想在上使用 forEach Node 的 childNodes 集合(作为 HTMLCollection,本身没有 forEach)。你可以这样做: Array.prototype.forEach.call(node.childNodes, (child) => { // 用 `child` 做一些事情 }); (但请注意,您可以只在 node.childNodes 上使用 for-of。)如果您要经常这样做,您可能希望将函数引用的副本抓取到变量中以供重用,例如:/ / (这可能都在一个模块或一些作用域函数中) const forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); // 然后稍后... forEach(node.childNodes, (child) => { // 对 `child` 做一些事情 });使用简单的 for 循环 很明显,简单的 for 循环适用于类似数组的对象。显式使用迭代器(ES2015+)参见#1。
您也许能够摆脱 for-in
(使用安全措施),但有了所有这些更合适的选项,就没有理由尝试了。
创建一个真正的数组
其他时候,您可能希望将类似数组的对象转换为真正的数组。这样做非常容易:
使用 Array.from Array.from (spec) | (MDN)(ES2015+,但很容易填充)从类似数组的对象创建一个数组,可选地首先通过映射函数传递条目。所以: const divs = Array.from(document.querySelectorAll("div")); ...从 querySelectorAll 中获取 NodeList 并从中创建一个数组。如果您要以某种方式映射内容,映射功能很方便。例如,如果您想获取具有给定类的元素的标签名称数组: // 典型用法(带有箭头函数): const divs = Array.from(document.querySelectorAll(".some-class" ), 元素 => element.tagName); // 传统函数(因为 `Array.from` 可以填充): var divs = Array.from(document.querySelectorAll(".some-class"), function(element) { return element.tagName; });使用扩展语法 (...) 也可以使用 ES2015 的扩展语法。与 for-of 一样,它使用对象提供的迭代器(参见上一节中的 #1): const trueArray = [...iterableObject];因此,例如,如果我们想将 NodeList 转换为真正的数组,使用扩展语法这变得非常简洁: const divs = [...document.querySelectorAll("div")];使用数组的 slice 方法我们可以使用数组的 slice 方法,它和上面提到的其他方法一样是“故意通用的”,因此可以用于类数组对象,例如:const trueArray = Array.prototype.slice。调用(arrayLikeObject);例如,如果我们想将 NodeList 转换为真正的数组,我们可以这样做: const divs = Array.prototype.slice.call(document.querySelectorAll("div")); (如果您仍然必须处理 IE8 [哎哟],将会失败;IE8 不允许您像这样使用主机提供的对象。)
主机提供的对象的警告
如果您将 Array.prototype
函数与 host-provided 类数组对象(例如,由浏览器而非 JavaScript 引擎提供的 DOM 集合等)一起使用,则 IE8 等过时的浏览器不一定能处理这样,如果您必须支持它们,请务必在您的目标环境中进行测试。但这不是现代浏览器的问题。 (对于非浏览器环境,自然要根据环境而定。)
注意:这个答案已经过时了。如需更现代的方法,请查看 the methods available on an array。感兴趣的方法可能是:
为每个
地图
筛选
压缩
减少
每一个
一些
在 JavaScript 中迭代数组的标准方法是普通的 for
循环:
var length = arr.length,
element = null;
for (var i = 0; i < length; i++) {
element = arr[i];
// Do something with element
}
但是请注意,这种方法只有在您有一个密集数组并且每个索引都被一个元素占用时才有效。如果数组是稀疏的,那么这种方法可能会遇到性能问题,因为您将遍历数组中真正不存在的许多索引。在这种情况下,for .. in
循环可能是一个更好的主意。 但是,您必须使用适当的保护措施来确保仅对数组的所需属性(即数组元素)进行操作,因为 for..in
循环也将在旧版中枚举浏览器,或者附加属性定义为 enumerable
。
在 ECMAScript 5 中,数组原型上会有一个 forEach 方法,但旧版浏览器不支持它。因此,为了能够始终如一地使用它,您必须有一个支持它的环境(例如,用于服务器端 JavaScript 的 Node.js),或者使用“Polyfill”。然而,这个功能的 Polyfill 是微不足道的,因为它使代码更容易阅读,所以它是一个很好的 polyfill。
document.getElementsByTagName("video")[28].playbackRate = 2.2
加速视频。如果我可以轻松地映射所有元素,那么我就可以避免识别哪个视频(例如,在这种情况下为索引 28)。有任何想法吗?
如果您使用 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] 处删除或插入一个项目),那么前向循环将跳过向左移动到位置 i 的项目,或重新处理第 i 个向右移动的项目。在传统的 for 循环中,您可以更新 i 以指向下一个需要处理的项目 - 1,但简单地反转迭代方向通常是一种更简单、更优雅的解决方案。
同样,在修改或删除嵌套的 DOM 元素时,反向处理可以规避错误。例如,考虑在处理其子节点之前修改父节点的 innerHTML。到达子节点时,它将与 DOM 分离,并在编写父级的 innerHTML 时被新创建的子级替换。
与其他一些可用选项相比,它的键入和阅读时间更短。虽然它输给了 forEach() 和 ES6 的 for ... of。
缺点:
它以相反的顺序处理项目。如果您要根据结果构建一个新数组,或者在屏幕上打印内容,那么输出自然会与原始顺序相反。
重复地将兄弟姐妹作为第一个孩子插入 DOM 以保持它们的顺序效率较低。 (浏览器将不得不不断地改变事情。)为了有效和有序地创建 DOM 节点,只需像往常一样循环向前和追加(并且还使用“文档片段”)。
反向循环让初级开发人员感到困惑。 (您可能会认为这是一种优势,具体取决于您的观点。)
我应该一直使用它吗?
一些开发人员默认使用反向 for 循环,除非有充分的理由向前循环。
尽管性能提升通常微不足道,但它有点尖叫:
“只要对列表中的每一项都这样做,我不在乎顺序!”
然而在实践中,这不是实际上是一个可靠的意图指示,因为它与您确实关心订单的那些场合没有区别,并且需要反向循环。因此,实际上需要另一个构造来准确表达“不关心”意图,这是目前大多数语言(包括 ECMAScript)中不可用的,但可以称为 forEachUnordered()
。
如果顺序无关紧要,并且效率是一个问题(在游戏或动画引擎的最内层循环中),那么使用反向 for 循环作为您的首选模式可能是可以接受的。请记住,在现有代码中看到反向 for 循环并不一定意味着顺序无关!
最好使用 forEach()
一般来说,对于清晰性和安全性更受关注的高级代码,我之前建议使用 Array::forEach
作为循环的默认模式(尽管这些天我更喜欢使用 for..of
)。选择 forEach
而不是反向循环的原因是:
读起来更清楚。
它表明 i 不会在块内移动(这总是隐藏在 long for 和 while 循环中的可能意外)。
它为您提供了一个免费的闭包范围。
它减少了局部变量的泄漏和与外部变量的意外碰撞(和突变)。
然后,当您确实在代码中看到反向 for 循环时,这暗示它被反转是有充分理由的(可能是上述原因之一)。看到传统的前向 for 循环可能表明可以发生转移。
(如果对意图的讨论对您没有意义,那么您和您的代码可能会从观看 Crockford 关于 Programming Style & Your Brain 的讲座中受益。)
现在使用 for..of 效果更好!
关于 for..of
或 forEach()
是否更可取存在争议:
为了获得最大的浏览器支持,for..of 需要一个用于迭代器的 polyfill,这会使您的应用程序执行速度稍慢,下载速度稍大。
出于这个原因(并鼓励使用 map 和 filter),一些前端风格指南完全禁止 for..of!
但是上述问题不适用于 Node.js 应用程序,现在 for..of 得到了很好的支持。
此外,等待在 forEach() 中不起作用。在这种情况下,使用 for..of 是最清晰的模式。
就个人而言,我倾向于使用看起来最容易阅读的任何内容,除非性能或缩小已成为主要问题。所以这些天我更喜欢使用 for..of
而不是 forEach()
,但在适用时我将始终使用 map
或 filter
或 find
或 some
。 (为了我的同事,我很少使用 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-out-of-bounds 未定义项目的任何问题。
为什么它不会在索引 0 之前停止迭代?当条件 i-- 计算为假值(当它产生 0 时)时,循环将停止迭代。诀窍在于,与--i 不同,尾随的i-- 运算符递减i,但产生递减之前的值。你的控制台可以证明这一点: > var i = 5; [我,我--,我]; [5, 5, 4] 所以在最后一次迭代中,i 之前是 1,而 i-- 表达式将其更改为 0,但实际上产生 1(真实),因此条件通过。在下一次迭代中 i-- 将 i 更改为 -1 但产生 0(错误),导致执行立即退出循环底部。在传统的前向 for 循环中,i++ 和 ++i 是可以互换的(正如 Douglas Crockford 指出的那样)。但是在反向 for 循环中,因为我们的减量也是我们的条件表达式,所以我们必须坚持使用 i-- 如果我们想处理索引 0 处的项目。
琐事
有些人喜欢在反向 for
循环中画一个小箭头,并以眨眼结束:
for (var i = array.length; i --> 0 ;) {
感谢 WYL 向我展示了反向 for 循环的好处和恐惧。
一些 C 风格的语言使用 foreach
循环枚举。在 JavaScript 中,这是通过 for..in
loop structure 完成的:
var index,
value;
for (index in obj) {
value = obj[index];
}
有一个问题。 for..in
将遍历对象的每个可枚举成员及其原型上的成员。为了避免读取通过对象原型继承的值,只需检查属性是否属于对象:
for (i in obj) {
if (obj.hasOwnProperty(i)) {
//do stuff
}
}
此外,ECMAScript 5 已向 Array.prototype
添加了一个 forEach
方法,该方法可用于使用回调枚举数组(polyfill 在文档中,因此您仍然可以将它用于旧版浏览器):
arr.forEach(function (val, index, theArray) {
//do stuff
});
请务必注意,当回调返回 false
时,Array.prototype.forEach
不会中断。 jQuery 和 Underscore.js 在 each
上提供了它们自己的变体,以提供可以短路的循环。
对于...的 |为每个 |地图
使用现代 JavaScript 语法遍历数组
const fruits = ['🍎', '🍋', '🍌' ]
👉🏽 为...的
for (const fruit of fruits) {
console.log(fruit) // '🍎', '🍋', '🍌'
}
👉🏽 为每个
fruits.forEach(fruit => {
console.log(fruit) // '🍎', '🍋', '🍌'
})
👉🏽地图
*与上述两个不同,map() 创建一个新数组并期望您在每次迭代后返回一些内容。
fruits.map(fruit => fruit) // ['🍎', '🍋', '🍌' ]
🛑 重要提示:由于 map() 旨在在每次迭代时返回一个值,因此它是转换数组中元素的理想方法:
fruits.map(fruit => 'cool ' + fruit) // ['cool 🍎', 'cool 🍋', 'cool 🍌' ]
另一方面,for...of 和 forEach() 不需要返回任何内容,这就是为什么我们通常使用它们来执行处理外部内容的逻辑任务。
可以这么说,你会在这两个中找到 if() 语句、副作用和日志记录活动。
👌🏾 提示:您还可以在 .map() 或 .forEach() 函数的每次迭代中拥有索引(以及整个数组)。
只需向他们传递额外的参数:
fruits.map((fruit, i) => i + ' ' + fruit)
// ['0 🍎', '1 🍋', '2 🍌' ]
fruits.forEach((f, i, arr) => {
console.log( f + ' ' + i + ' ' + arr )
})
// 🍎 0 🍎, 🍋, 🍌,
// 🍋 1 🍎, 🍋, 🍌,
// 🍌 2 🍎, 🍋, 🍌,
如果要循环遍历数组,请使用标准的三部分 for
循环。
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
}
您可以通过缓存 myArray.length
或向后迭代它来获得一些性能优化。
如果您不介意清空数组:
var x;
while(x = y.pop()){
alert(x); //do something
}
x
将包含 y
的最后一个值,并将从数组中删除。您还可以使用 shift()
,它会从 y
中提供和删除第一项。
forEach 实现 (see in 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
}
);
我知道这是一个旧帖子,并且已经有很多很棒的答案。为了更完整一点,我想我会使用 AngularJS 添加另一个。当然,这只适用于你使用 Angular 的情况,很明显,尽管如此我还是想把它说出来。
angular.forEach
接受 2 个参数和一个可选的第三个参数。第一个参数是要迭代的对象(数组),第二个参数是迭代器函数,可选的第三个参数是对象上下文(在循环内部基本上称为“this”。
有不同的方法可以使用 angular 的 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 循环。现在,我将在 cons 部分讨论效率,因为 forEach 循环比 for 循环慢得多。我作为专业人士提到这一点是因为保持一致和标准化很好。
考虑以下 2 个嵌套循环,它们的作用完全相同。假设我们有 2 个对象数组,每个对象包含一个结果数组,每个结果都有一个 Value 属性,它是一个字符串(或其他)。假设我们需要遍历每个结果,如果它们相等,则执行一些操作:
angular.forEach(obj1.results, function(result1) {
angular.forEach(obj2.results, function(result2) {
if (result1.Value === result2.Value) {
//do something
}
});
});
//exact same with a for loop
for (var i = 0; i < obj1.results.length; i++) {
for (var j = 0; j < obj2.results.length; j++) {
if (obj1.results[i].Value === obj2.results[j].Value) {
//do something
}
}
}
当然,这是一个非常简单的假设示例,但我已经使用第二种方法编写了三重嵌入式 for 循环,并且很难阅读和编写。
缺点
效率。就此而言,angular.forEach 和原生 forEach 都比正常的 for 循环慢得多……大约慢 90%。所以对于大数据集,最好还是坚持原生的for循环。
没有中断、继续或返回支持。 continue 实际上是由“意外”支持的,在 angular.forEach 中继续,你简单地放一个 return;函数中的语句,如 angular.forEach(array, function(item) { if (someConditionIsTrue) return; });这将导致它继续退出该迭代的功能。这也是由于原生 forEach 也不支持 break 或 continue 的事实。
我敢肯定还有其他各种优点和缺点,请随时添加您认为合适的任何内容。我觉得,最重要的是,如果您需要效率,请坚持使用本机 for
循环来满足您的循环需求。但是,如果您的数据集较小并且可以放弃一些效率以换取可读性和可写性,那么一定要在那个坏男孩身上扔一个 angular.forEach
。
从 ECMAScript 6 开始:
list = [0, 1, 2, 3] for (let obj of list) { console.log(obj) }
其中 of
避免了与 in
相关的奇怪之处,并使其像任何其他语言的 for
循环一样工作,并且 let
将 i
绑定在循环内,而不是在函数内。
当只有一个命令时(例如在上面的示例中),可以省略大括号 ({}
)。
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
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
//!!!
现在一个简单的解决方案是使用 underscore.js library。它提供了许多有用的工具,例如 each
,并且会自动将作业委托给本地 forEach
(如果可用)。
A CodePen example 它的工作原理是:
var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});
也可以看看
本机 Array.prototype.forEach() 的文档。
在 for_each...in (MDN) 中解释说,对于每个(对象中的变量)作为 ECMA-357(EAX)标准的一部分已被弃用。
for...of (MDN) 描述了使用 for(对象变量)作为 Harmony(ECMAScript 6)提案的一部分的下一种迭代方式。
原生 JavaScript 中没有任何 for each
循环。您可以使用库来获得此功能(我推荐 Underscore.js),在循环中使用简单的 for
。
for (var instance in objects) {
...
}
但是,请注意,可能有理由使用更简单的 for
循环(请参阅 Stack Overflow 问题 Why is using “for…in” with array iteration such a bad idea?)
var instance;
for (var i=0; i < objects.length; i++) {
var instance = objects[i];
...
}
ECMAScript 5(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 - 顾名思义,它通过调用传入当前元素和上一次执行结果的给定函数将数组缩减为单个值。
[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 - 除了 filter 返回一个数组,其中的元素对给定函数返回 true 之外,与every 非常相似。
// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
return (elem % 2 == 0)
});
// Output: [2,4,6]
在 JavaScript 中有几种方法可以遍历数组,如下所示:
因为 - 这是最常见的一种。完整的循环代码块
var 语言 = ["Java", "JavaScript", "C#", "Python"];变量 i,len,文本; for (i = 0, len = languages.length, text = ""; i < len; i++) { text += 语言[i] + "
"; } document.getElementById("example").innerHTML = text;
while - 在条件通过时循环。这似乎是最快的循环
变种文字=“”;变量 i = 0; while (i < 10) { text += i + ") something
";我++; } document.getElementById("example").innerHTML = text;
do/while - 当条件为真时也循环通过一段代码,将至少运行一次
变种文字=“”变种我=0;做 { text += i + ") 某事
";我++; } 而 (i < 10); document.getElementById("example").innerHTML = text;
函数循环 - forEach
、map
、filter
,还有 reduce
(它们循环通过函数,但如果您需要对数组执行某些操作,则使用它们等。
// 例如,在这种情况下,我们循环遍历数字并使用映射函数将它们加倍 var numbers = [65, 44, 12, 4]; document.getElementById("example").innerHTML = numbers.map(function(num){return num * 2});
有关数组函数式编程的更多信息和示例,请查看博文Functional programming in JavaScript: map, filter and reduce。
这是一个索引从 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 标签变为绿色
使用字符串作为函数时要注意:该函数是在上下文之外创建的,并且应该仅在您确定变量范围的情况下使用。否则,最好传递范围更直观的函数。
forEach
没有内置功能。要中断执行,请使用 Array#some
,如下所示:
[1,2,3].some(function(number) {
return number === 1;
});
这是因为 some
在任何以数组顺序执行的回调返回 true 时立即返回 true,从而使其余部分的执行短路。 Original Answer 请参阅 some 的数组原型
我还想将其添加为反向循环的组合和上面的答案,供那些也喜欢这种语法的人使用。
var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
console.log(item);
}
优点:
这样做的好处是:您已经在第一个引用中有了引用,以后不需要再用另一行声明。在通过对象数组循环时很方便。
缺点:
只要引用为假-假(未定义等),这就会中断。虽然它可以用作优势。但是,这会使阅读变得有点困难。并且还取决于浏览器,它可以“不”优化为比原始浏览器运行得更快。
使用 $.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];
在 ECMAScript 6 destructuring 和 spread operator 中使用循环
事实证明,展开运算符的解构和使用对 ECMAScript 6 的新手非常有用,因为它更易于阅读/更美观,尽管一些 JavaScript 资深人士可能认为它很混乱。小辈或其他一些人可能会觉得它很有用。
以下示例将使用 for...of 语句和 .forEach 方法。示例 6、7 和 8 可以与任何功能循环一起使用,例如 .map、.filter、.reduce、.sort、.every、.some。有关这些方法的更多信息,请查看 Array Object。
示例 1: 正常 for...of
循环 - 这里没有技巧。
让 arrSimple = ['a', 'b', 'c']; for (let letter of arrSimple) { console.log(letter); }
示例 2:将单词拆分为字符
让 arrFruits = ['apple', 'orange', 'banana']; for (let [firstLetter, ...restOfTheWord] of arrFruits) { // 使用扩展运算符创建浅拷贝 let [lastLetter] = [...restOfTheWord].reverse(); console.log(firstLetter, lastLetter, restOfTheWord); }
示例 3: 使用 key
和 value
循环
// 让 arrSimple = ['a', 'b', 'c']; // 而不是按照示例在 `i` 中保留索引 `for(let i = 0 ; i
示例 4:内联获取对象属性
让 arrWithObjects = [{ name: 'Jon', age: 32 }, { name: 'Elise', age: 33 } ]; for (let { name, age: aliasForAge } of arrWithObjects) { console.log(name, aliasForAge); }
示例 5:获取所需内容的深层对象属性
让 arrWithObjectsWithArr = [{ name: 'Jon', age: 32, tags: ['driver', 'chef', 'jogger'] }, { name: 'Elise', age: 33, tags: ['best chef' , '歌手', '舞者'] } ]; for (let { name, tags: [firstItemFromTags, ...restOfTags] } of arrWithObjectsWithArr) { console.log(name, firstItemFromTags, restOfTags); }
示例 6: 示例 3 与 .forEach
一起使用
让 arrWithIndex = [ [0, 'a'], [1, 'b'], [2, 'c'], ]; // 这里不要混淆,`forEachIndex` 是真正的索引 // `mappedIndex` 是由“另一个用户”创建的,所以你不能真正相信它 arrWithIndex.forEach(([mappedIndex, item], forEachIndex) = > { console.log(forEachIndex, mappedIndex, item); });
示例 7: 示例 4 与 .forEach
一起使用
让 arrWithObjects = [{ name: 'Jon', age: 32 }, { name: 'Elise', age: 33 } ]; // 注意:使用速记函数时解构对象 // 需要用括号括起来 arrWithObjects.forEach( ({ name, age: aliasForAge }) => { console.log(name, aliasForAge) });
示例 8: 示例 5 与 .forEach
一起使用
让 arrWithObjectsWithArr = [{ name: 'Jon', age: 32, tags: ['driver', 'chef', 'jogger'] }, { name: 'Elise', age: 33, tags: ['best chef' , '歌手', '舞者'] } ]; arrWithObjectsWithArr.forEach(({ name, tags: [firstItemFromTags, ...restOfTags] }) => { console.log(name, firstItemFromTags, restOfTags); });
表现
今天 (2019-12-18) 我在我的 macOS v10.13.6 (High Sierra)、Chrome v 79.0、Safari v13.0.4 和 Firefox v71.0(64 位)上执行测试 - 关于优化的结论(和 {2 } 通常不值得将其引入代码,因为好处很小,但代码复杂性会增加)。
看起来传统的 for i (Aa) 是在所有浏览器上编写快速代码的不错选择。
其他解决方案,如 for-of (Ad),都在 C 组中......通常比 Aa 慢 2-10 倍(甚至更多),但对于小型阵列,可以使用它 - 为了增加代码清晰。
在 n (Ab, Bb, Be) 中缓存数组长度的循环有时更快,有时则不然。可能编译器会自动检测这种情况并引入缓存。缓存版本和无缓存版本(Aa、Ba、Bd)之间的速度差异约为 1%,因此看起来引入 n 是一种微优化。
从最后一个数组元素(Ac,Bc)开始循环的 i-like 解决方案通常比前向解决方案慢约 30% - 可能原因是 CPU 内存缓存的工作方式 - 前向内存读取对于 CPU 更优化缓存)。建议不要使用此类解决方案。
细节
在测试中,我们计算数组元素的总和。我对小数组(10 个元素)和大数组(1M 个元素)进行了测试,并将它们分为三组:
A - 用于测试
- while 测试
- 其他/替代方法
让 arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; //让 arr = Array.from(Array(1000000), (x, i) => i%10);函数 Aa(a, s=0) { for(let i=0; i 此片段仅展示用于基准测试的代码 - 它不执行测试本身
跨浏览器结果
所有测试浏览器的结果
https://i.stack.imgur.com/bSXp2.png
包含 10 个元素的数组
铬的结果。您可以在您的机器 here 上执行测试。
https://i.stack.imgur.com/VuCMw.png
包含 1,000,000 个元素的数组
铬的结果。您可以在您的机器上执行测试 here
https://i.stack.imgur.com/8JQf6.png
概括:
在遍历数组时,我们通常希望完成以下目标之一:
我们想要遍历数组并创建一个新数组:Array.prototype.map 我们想要遍历数组而不创建新数组:Array.prototype.forEach for..of 循环
在 JavaScript 中,有很多方法可以实现这两个目标。但是,有些比其他更方便。下面你可以找到一些在 JavaScript 中完成数组迭代的常用方法(最方便的 IMO)。
创建新数组:地图
map()
是位于 Array.prototype
上的函数,它可以转换数组的每个元素,然后返回一个 new 数组。 map()
将回调函数作为参数并按以下方式工作:
让 arr = [1, 2, 3, 4, 5]; let newArr = arr.map((element, index, array) => { return element * 2; }) console.log(arr);控制台.log(newArr);
我们作为参数传递给 map()
的回调会为每个元素执行。然后返回一个与原始数组长度相同的数组。在这个新的数组元素中,作为参数传入 map()
的回调函数会转换此元素。
map
与另一种循环机制(如 forEach
和 for..of
循环)之间的明显区别在于 map
返回一个新数组并保持旧数组完好无损(除非您显式操作它与 splice
类似的想法)。
另请注意,map
函数的回调将当前迭代的索引号作为第二个参数提供。此外,第三个参数是否提供了调用 map
的数组?有时这些属性可能非常有用。
使用 forEach 循环
forEach
是一个位于 Array.prototype
上的函数,它将回调函数作为参数。然后它为数组中的每个元素执行这个回调函数。与 map()
函数相比,forEach 函数不返回任何内容 (undefined
)。例如:
让 arr = [1, 2, 3, 4, 5]; arr.forEach((element, index, array) => { console.log(element * 2); if (index === 4) { console.log(array) } // index 和 oldArray 作为第二个和回调的第三个参数 }) console.log(arr);
与 map
函数一样,forEach
回调提供当前迭代的索引号作为第二个参数。另外,第三个参数是否提供了调用 forEach
的数组?
使用 for..of 循环遍历元素
for..of
循环遍历数组(或任何其他可迭代对象)的每个元素。它以下列方式工作:
让 arr = [1, 2, 3, 4, 5]; for(让 arr 的元素) { console.log(element * 2); }
在上面的例子中,element
代表一个数组元素,arr
是我们要循环的数组。请注意,名称 element
是任意的,我们可以选择任何其他名称,例如 'el' 或在适用时更具声明性的名称。
不要将 for..in
循环与 for..of
循环混淆。 for..in
将循环遍历数组的所有可枚举属性,而 for..of
循环将仅循环遍历数组元素。例如:
让 arr = [1, 2, 3, 4, 5]; arr.foo = 'foo'; for(让 arr 的元素) { console.log(element); } for(let element in arr) { console.log(element); }
最接近您的想法的一种方法是使用 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]
根据 ECMAScript 6 (ES6) 和 ECMAScript 2015 的新更新功能,您可以在循环中使用以下选项:
for 循环
for(var i = 0; i < 5; i++){
console.log(i);
}
// Output: 0,1,2,3,4
for...in 循环
let obj = {"a":1, "b":2}
for(let k in obj){
console.log(k)
}
// Output: a,b
Array.forEach()
let array = [1,2,3,4]
array.forEach((x) => {
console.log(x);
})
// Output: 1,2,3,4
for...of 循环
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
做...while循环
let x = 0
do{
console.log(x)
x++
}while(x < 5)
// Output: 1,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 文件在运行,你应该颠倒 index 和 value 参数的位置
$("#ul>li").each(function(**index, value**){
console.log("value of the looped element" + value);
console.log("index of the looped element" + index);
});
您可以像这样调用 forEach:
forEach
将遍历您提供的数组,并且对于每次迭代,它都有 element
保存该迭代的值。如果您需要索引,您可以通过在 forEach 的回调函数中将 i
作为第二个参数传递来获取当前索引。
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
}
如果您想使用 forEach()
,它看起来像 -
theArray.forEach ( element => {
console.log(element);
});
如果您想使用 for()
,它看起来像 -
for(let idx = 0; idx < theArray.length; idx++){
let element = theArray[idx];
console.log(element);
}
如果要使用箭头函数循环遍历对象数组:
让 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'); })
const a = ["a", "b"]; a.example = 42;
该数组具有三个属性(除了所有数组都具有的属性),其名称为字符串"0"
、"1"
和"example"
。名为"example"
的属性是一个非元素属性。另外两个是元素属性,因为它们代表数组的元素。var arr = new Array();
中创建的数组被丢弃并由您在arr = {"test":"testval", "test2":"test2val"};
中创建的对象替换。该代码应该只是var arr = {"test":"testval", "test2":"test2val"};
(嗯,不是var
,而是let
或const
)。当然,根据某些定义,对象可能被认为是关联数组,但在其他定义中,它们不是,我避免在 JS 中使用该术语,因为它在 PHP 中具有特定含义,这是 JavaScript 相邻的,因为它们都在 Web 工作中被大量使用.theObject.propName
、theObject["propName"]
、theObject[propKeySymbol]
等)。索引访问的概念对对象没有用处。不过,这是非常间接的可能。 🙂 对象属性现在确实有顺序(ES2015+,在后来的几个规范中进行了调整),但是顺序很复杂,它取决于属性创建的顺序,属性键的类型,...Reflect.ownKeys
会按顺序提供对象的 own 属性键数组(跳过继承的属性键)。因此,如果这适合用例,您可以从中获取一个数组 (const keys = Reflect.ownKeys(theObject);
)。 ...theObject[keys[index]]
。不过,我无法想象它的用例。如果您想要对象的所有属性(继承的 + 自己的),那就更复杂了,您必须循环通过原型链附加您还没有看到的属性(Set
在那里可能很有用,因为Set
是严格的按值插入排序并且不允许重复):...