提问人:Dante1986 提问时间:2/17/2012 最后编辑:T.J. CrowderDante1986 更新时间:5/25/2023 访问量:5246894
在 JavaScript 中循环(针对每个数组)
Loop (for each) over an array in JavaScript
答:
TL;博士
您最好的选择通常是
- 循环(仅限 ES2015+;规格 |MDN的)- 简单友好
for-of
async
for (const element of theArray) { // ...use `element`... }
forEach
(仅限 ES5+;规格 |MDN的)(或其亲戚等) - 不友好(但查看详细信息)some
async
theArray.forEach(element => { // ...use `element`... });
- 一个简单的老式循环 - -友好
for
async
for (let index = 0; index < theArray.length; ++index) { const element = theArray[index]; // ...use `element`... }
- (很少)有保障措施 - -友好
for-in
async
for (const propertyName in theArray) { if (/*...is an array element property (see below)...*/) { const element = theArray[propertyName]; // ...use `element`... } }
- 循环(仅限 ES2015+;规格 |MDN的)- 简单友好
一些快速的“不要”:
但还有很多东西需要探索,请继续阅读......
JavaScript 具有强大的语义,用于遍历数组和类似数组的对象。我把答案分成了两部分:真实数组的选项,以及类似数组的东西的选项,例如对象、其他可迭代对象 (ES2015+)、DOM 集合等。arguments
好的,让我们看看我们的选择:
对于实际数组
您有五个选项(两个基本上永久支持,另一个由 ECMAScript 5 [“ES5”] 添加,另外两个在 ECMAScript 2015 中添加(“ES2015”,又名“ES6”):
- 使用(隐式使用迭代器)(ES2015+)
for-of
- 使用及相关 (ES5+)
forEach
- 使用简单的循环
for
- 正确使用
for-in
- 显式使用迭代器 (ES2015+)
(你可以在这里看到那些旧的规格:ES5、ES2015,但两者都已被取代;当前编辑的草稿总是在这里。
详:
1. 使用(隐式使用迭代器) (ES2015+)for-of
ES2015 为 JavaScript 添加了迭代器和可迭代对象。数组是可迭代的(字符串、s 和 s,以及 DOM 集合和列表也是可迭代的,稍后将看到)。可迭代对象为其值提供迭代器。new 语句遍历迭代器返回的值:Map
Set
for-of
const a = ["a", "b", "c"];
for (const element of a) { // You can use `let` instead of `const` if you like
console.log(element);
}
// a
// b
// c
没有比这更简单的了!在幕后,它从数组中获取一个迭代器,并循环遍历迭代器返回的值。数组提供的迭代器按从头到尾的顺序提供数组元素的值。
请注意,如何限定每个循环迭代的范围;尝试在循环结束后使用将失败,因为它不存在于循环体之外。element
element
从理论上讲,循环涉及多个函数调用(一个用于获取迭代器,然后一个用于从中获取每个值)。即使这是真的,也没什么好担心的,函数调用在现代 JavaScript 引擎中非常便宜(在我研究它之前,它困扰了我 [下文];详细信息)。但除此之外,JavaScript 引擎在处理数组等原生迭代器时会优化这些调用(在性能关键代码中)。for-of
forEach
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);
console.log(message);
}
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects
请注意单词的显示方式,每个单词之前都有延迟。
这是一个编码风格的问题,但这是我在循环遍历任何可迭代的东西时首先要做的事情。for-of
2. 使用及相关forEach
在任何可以访问 ES5 添加的功能的现代环境中(因此,不是 IE8),您可以使用 (spec |MDN的)如果您只处理同步代码(或者您不需要等待异步进程在循环期间完成):Array
forEach
const a = ["a", "b", "c"];
a.forEach((element) => {
console.log(element);
});
forEach
接受回调函数,以及调用该回调时要使用的值(可选),上面未使用。对数组中的每个元素按顺序调用回调,跳过稀疏数组中不存在的元素。虽然我只使用了上面的一个参数,但回调是用三个参数调用的:该迭代的元素、该元素的索引以及对要迭代的数组的引用(以防您的函数还没有它)。this
Like 的优点是不必在包含作用域中声明索引变量和值变量;在本例中,它们作为 iteration 函数的参数提供,因此很好地限定为该迭代。for-of
forEach
与 不同的是,它的缺点是它不理解函数和 。如果使用函数作为回调,则不会等待该函数的 promise 结算后再继续。下面是 using instead 的示例 - 请注意初始延迟,但随后所有文本都会立即出现,而不是等待:for-of
forEach
async
await
async
forEach
async
for-of
forEach
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
// INCORRECT, doesn't wait before continuing,
// doesn't handle promise rejections
messages.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的)- 回调首次返回真实值时停止循环filter
(规格 |MDN的)- 创建一个新数组,其中包含回调返回真实值的元素,省略不返回真实值的元素map
(规格 |MDN的)- 根据回调返回的值创建一个新数组reduce
(规格 |MDN的)- 通过重复调用回调来构建一个值,传入以前的值;有关详细信息,请参阅规格reduceRight
(规格 |MDN的)- 类似 ,但按降序而不是升序工作reduce
与 一样,如果您使用函数作为回调,则这些函数都不会等待函数的 promise 结算。这意味着:forEach
async
- 使用函数回调永远不适合 ,并且因为它们会将返回的 promise 视为真实值;他们不会等到承诺落定后再使用履行价值。
async
every
some
filter
- 如果目标是将某个数组转换为一个 promise 数组,则通常适合使用 ,也许是为了传递给其中一个 promise 组合器函数(Promise.all、Promise.race、
promise.allSettled
或Promise.any
)。async
map
- 很少将函数回调与 或 一起使用,因为(再次)回调将始终返回一个 promise。但是有一种习惯,即从使用 () 的数组构建 promise 链,但通常在这些情况下,函数中的 or 循环会更清晰、更易于调试。
async
reduce
reduceRight
reduce
const promise = array.reduce((p, element) => p.then(/*...something using `element`...*/));
for-of
for
async
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 之前,循环变量必须存在于包含作用域中,因为只有函数级作用域,而没有块级作用域。但正如您在上面的示例中看到的那样,您可以在 to 中将变量范围限定为仅循环。当你这样做时,每个循环迭代都会重新创建变量,这意味着在循环体中创建的闭包会保留对该特定迭代的引用,这解决了旧的“循环中的闭包”问题:var
let
for
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>
在上面,如果单击第一个,则获得“索引为:0”,如果单击最后一个,则获得“索引为:4”。如果您使用 instead (您总是会看到“索引为:5”),则这不起作用。var
let
像 一样,循环在函数中工作得很好。下面是前面使用循环的示例: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 array
const 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) && // explained
name <= 4294967294 // below
) {
const element = a[name];
console.log(a[name]);
}
}
请注意以下三项检查:
该对象具有该名称的自己的属性(不是从原型继承的属性;此检查也通常写为 但 ES2022 添加了
Object.hasOwn
,这可能更可靠),并且a.hasOwnProperty(name)
名称全部为十进制数字(例如,正常字符串形式,而不是科学记数法),以及
当强制转换为一个数字时,名称的值为 <= 2^32 - 2(即 4,294,967,294)。这个数字从何而来?它是规范中数组索引定义的一部分。其他数字(非整数、负数、大于 2^32 - 2 的数字)不是数组索引。它是 2^32 - 2 的原因是,这使得最大索引值低于 2^32 - 1,这是数组可以具有的最大值。(例如,数组的长度适合 32 位无符号整数。
length
...话虽如此,但大多数代码只执行检查。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 do
const 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
隐式使用迭代器,为你完成所有 scut 工作。有时,您可能希望显式使用迭代器。它看起来像这样:
const a = ["a", "b", "c"];
const it = a.values(); // Or `const it = a[Symbol.iterator]();` if you like
let entry;
while (!(entry = it.next()).done) {
const element = entry.value;
console.log(element);
}
迭代器是与规范中的迭代器定义匹配的对象。每次调用它时,它的方法都会返回一个新的结果对象。result 对象有一个属性 ,告诉我们它是否已完成,以及一个具有该迭代值的属性。(如果是可选的,则为可选的。next
done
value
done
false
value
undefined
你得到的东西因迭代器而异。在数组上,默认迭代器提供每个数组元素的值(、、和前面的示例中)。数组还具有其他三个返回迭代器的方法: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
对于类似数组的对象
除了真正的数组之外,还有一些类似数组的对象,它们具有一个属性和具有全数字名称的属性:NodeList
实例、HTMLCollection
实例、对象等。我们如何循环浏览它们的内容?length
arguments
使用上述大多数选项
上面的数组方法中至少有一些,可能大部分甚至全部都同样适用于类数组对象:
使用
for-of
(隐式使用迭代器)(ES2015+)for-of
使用对象提供的迭代器(如果有)。这包括主机提供的对象(如 DOM 集合和列表)。例如,方法中的实例和两者中的 s 实例都支持迭代。(这由 HTML 和 DOM 规范非常微妙地定义。基本上,任何具有索引访问权限和索引访问权限的对象都是自动可迭代的。它不必被标记;这仅用于除了可迭代之外还支持 、 、 和 方法的集合。 确实; 没有,但两者都是可迭代的。HTMLCollection
getElementsByXYZ
NodeList
querySelectorAll
length
iterable
forEach
values
keys
entries
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>
使用
forEach
和相关 (ES5+)on 上的各种函数是“有意泛型的”,可以通过 (spec |MDN的)或 (spec |MDN)。(如果您必须处理 IE8 或更早版本 [哎哟],请参阅本答案末尾的“主机提供的对象的注意事项”,但这不是模糊现代浏览器的问题。
Array.prototype
Function#call
Function#apply
假设你想在 的集合上使用(作为一个 ,它本身没有)。你会这样做:
forEach
Node
childNodes
HTMLCollection
forEach
Array.prototype.forEach.call(node.childNodes, (child) => { // Do something with `child` });
(但请注意,您可以只在 上使用 。
for-of
node.childNodes
如果你要经常这样做,你可能希望将函数引用的副本抓取到变量中以便重用,例如:
// (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` });
使用简单的
for
循环也许很明显,一个简单的循环适用于类似数组的对象。
for
显式使用迭代器 (ES2015+)
参见 #1。
您也许可以(有保障措施)逃脱惩罚,但有了所有这些更合适的选择,就没有理由尝试了。for-in
创建一个真正的数组
其他时候,您可能希望将类似数组的对象转换为真正的数组。这样做非常容易:
使用
Array.from
Array.from
(规格) |(MDN) (ES2015+,但很容易填充)从类似数组的对象创建一个数组,可以选择先通过映射函数传递条目。所以:const divs = Array.from(document.querySelectorAll("div"));
...从 中获取并从中创建一个数组。
NodeList
querySelectorAll
如果您要以某种方式映射内容,映射功能非常方便。例如,如果你想获取具有给定类的元素的标签名称数组:
// 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; });
使用传播语法 (
...
)也可以使用 ES2015 的 spread 语法。像 一样,它使用对象提供的迭代器(参见上一节中的 #1):
for-of
const trueArray = [...iterableObject];
例如,如果我们想将 a 转换为真正的数组,使用扩展语法,这将变得非常简洁:
NodeList
const divs = [...document.querySelectorAll("div")];
使用数组的
slice
方法我们可以使用数组的
slice
方法,它与上面提到的其他方法一样是“有意泛型的”,因此可以与类似数组的对象一起使用,如下所示:const trueArray = Array.prototype.slice.call(arrayLikeObject);
例如,如果我们想将 a 转换为真数组,我们可以这样做:
NodeList
const divs = Array.prototype.slice.call(document.querySelectorAll("div"));
(如果你仍然要处理IE8[哎哟],会失败;IE8 不允许你像这样使用主机提供的对象。
this
主机提供的对象的注意事项
如果您将函数与主机提供的类似数组的对象一起使用(例如,DOM 集合等由浏览器而不是 JavaScript 引擎提供),则过时的浏览器(如 IE8)不一定以这种方式处理,因此,如果您必须支持它们,请务必在目标环境中进行测试。但对于模糊的现代浏览器来说,这不是问题。(对于非浏览器环境,这自然取决于环境。Array.prototype
评论
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
theObject.propName
theObject["propName"]
theObject[propKeySymbol]
Reflect.ownKeys
按顺序提供对象自己的属性键数组(跳过继承的属性键)。因此,如果这适合用例,您可以从该 () 中获取一个数组。...const keys = Reflect.ownKeys(theObject);
let keys = []; for (let obj = theObject; obj; obj = Object.getPrototypeOf(obj)) { keys.push(...Reflect.ownKeys(obj)); } keys = [...new Set(keys)];
注意:这个答案已经过时了。有关更现代的方法,请查看数组上可用的方法。感兴趣的方法可能是:
- forEach
- 地图
- 滤波器
- 拉链
- 减少
- 每
- 一些
在 JavaScript 中迭代数组的标准方法是 vanilla -loop:for
var length = arr.length,
element = null;
for (var i = 0; i < length; i++) {
element = arr[i];
// Do something with element
}
但是请注意,这种方法仅在您有一个密集数组并且每个索引都被一个元素占用时才有效。如果数组是稀疏的,那么使用这种方法可能会遇到性能问题,因为你将遍历数组中实际上不存在的许多索引。在这种情况下,-loop 可能是一个更好的主意。但是,您必须使用适当的保护措施来确保仅对数组的所需属性(即数组元素)执行操作,因为 -loop 也将在旧版浏览器中枚举,或者如果其他属性定义为 。for .. in
for..in
enumerable
在 ECMAScript 5 中,数组原型上会有一个 forEach 方法,但在旧版浏览器中不支持它。因此,为了能够始终如一地使用它,您必须有一个支持它的环境(例如,服务器端 JavaScript 的 Node.js),或者使用“Polyfill”。但是,此功能的 Polyfill 是微不足道的,并且由于它使代码更易于阅读,因此它是一个很好的 polyfill。
评论
document.getElementsByTagName("video")[28].playbackRate = 2.2
如果要遍历数组,请使用标准的三部分循环。for
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
}
您可以通过缓存或向后迭代来获得一些性能优化。myArray.length
原生 JavaScript 中没有任何循环。您可以使用库来获取此功能(我推荐 Underscore.js),使用简单的 in 循环。for each
for
for (var instance in objects) {
...
}
但是,请注意,可能有理由使用更简单的循环(请参阅 Stack Overflow 问题:为什么使用“for...in“与数组迭代这样的坏主意?for
)
var instance;
for (var i=0; i < objects.length; i++) {
var instance = objects[i];
...
}
某些 C 样式语言用于循环枚举。在 JavaScript 中,这是通过 for..in
循环结构:foreach
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
方法,可用于使用 calback 对数组进行枚举(polyfill 在文档中,因此您仍然可以将其用于较旧的浏览器):Array.prototype
arr.forEach(function (val, index, theArray) {
//do stuff
});
需要注意的是,当回调返回时,它不会中断。jQuery 和 Underscore.js 提供了自己的变体,以提供可以短路的循环。Array.prototype.forEach
false
each
如果您使用的是 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
将包含 的最后一个值,它将从数组中删除。您还可以使用 which 将提供和删除第一项。y
shift()
y
forEach 实现(参见 jsFiddle):
function forEach(list,callback) {
var length = list.length;
for (var n = 0; n < length; n++) {
callback.call(list[n]);
}
}
var myArray = ['hello','world'];
forEach(
myArray,
function(){
alert(this); // do something
}
);
现在一个简单的解决方案是使用 underscore.js 库。它提供了许多有用的工具,例如,如果可用,它会自动将作业委托给本机。each
forEach
var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});
另请参阅
- 原生
Array.prototype.forEach()
的文档。 - 在for_each...在 (MDN) 中,它被解释为 ECMA-357 (EAX) 标准的一部分被弃用。
for each (variable in object)
- 为。。。of (MDN) 描述了 Harmony (ECMAScript 6) 提案中用作的下一种迭代方式。
for (variable of object)
循环可能不是最佳选择。为什么?如果你有这个: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
这是 NON-sparse 列表的迭代器,其中索引从 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 标签变为绿色
使用字符串作为函数时要注意:该函数是脱离上下文创建的,只有在确定变量范围的情况下才应使用。否则,最好传递范围更直观的函数。
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];
向后循环
我认为反向for循环值得一提:
for (var i = array.length; i--; ) {
// process array[i]
}
优势:
- 您不需要声明一个临时变量,也不需要在每次迭代时进行比较,其中任何一个都可能是一分钟的优化。
len
array.length
- 以相反的顺序从 DOM 中删除同级通常更有效。(浏览器需要减少其内部数组中元素的移动。
- 如果在循环时修改数组,在索引 i 处或之后(例如,在 处删除或插入一个项目),则前向循环将跳过左移到位置 i 的项目,或重新处理右移的第 i 个项目。在传统的 for 循环中,您可以更新 i 以指向下一个需要处理的项目 - 1,但简单地反转迭代方向通常是一种更简单、更优雅的解决方案。
array[i]
- 同样,在修改或删除嵌套的 DOM 元素时,反向处理可以规避错误。例如,请考虑在处理父节点的子节点之前修改其 innerHTML。当到达子节点时,它将与 DOM 分离,在编写父节点的 innerHTML 时被新创建的子节点替换。
- 与其他一些可用的选项相比,它的键入和阅读时间更短。虽然它输给了 ES6 的.
forEach()
for ... of
弊:
- 它以相反的顺序处理项目。如果您从结果构建一个新数组,或者在屏幕上打印内容,则输出自然会相对于原始顺序相反。
- 重复将兄弟姐妹作为第一个子项插入 DOM 以保留其顺序的效率较低。(浏览器将不得不不断向右移动内容。要高效且有序地创建 DOM 节点,只需像往常一样向前循环和追加(并使用“文档片段”)。
- 反向循环会让初级开发人员感到困惑。(你可能会认为这是一个优势,这取决于你的前景。
我应该一直使用它吗?
一些开发人员默认使用反向 for 循环,除非有充分的理由进行正向循环。
虽然性能提升通常微不足道,但它有点尖叫:
“只要对清单上的每一件商品都这样做,我不在乎订单!”
然而,在实践中,这实际上并不是一个可靠的意图指示,因为它与那些你确实关心顺序的场合没有区别,并且确实需要反向循环。因此,事实上,需要另一种结构来准确表达“不在乎”的意图,这是目前大多数语言(包括 ECMAScript)中不可用的东西,但可以称为 .forEachUnordered()
如果顺序无关紧要,并且效率是一个问题(在游戏或动画引擎的最内层循环中),那么使用反向 for 循环作为首选模式可能是可以接受的。请记住,在现有代码中看到反向 for 循环并不一定意味着顺序无关紧要!
最好使用 forEach()
一般来说,对于更高级别的代码,清晰度和安全性是更大的关注点,我以前建议使用 Array::forEach
作为循环的默认模式(尽管现在我更喜欢使用 )。首选反向循环的原因是:for..of
forEach
- 阅读起来更清晰。
- 它表明 i 不会在块内移动(这总是隐藏在长循环中的可能惊喜)。
for
while
- 它为您提供了关闭的自由范围。
- 它减少了局部变量的泄漏以及与外部变量的意外碰撞(和突变)。
然后,当您在代码中看到反向 for 循环时,这暗示它被反转是有充分理由的(也许是上面描述的原因之一)。看到传统的正向循环可能表明可以发生移位。
(如果关于意图的讨论对你来说没有意义,那么你和你的代码可能会从观看 Crockford 关于编程风格和你的大脑的讲座中受益。
现在更适合用于..之!
关于是否或更可取存在争议:for..of
forEach()
为了获得最大的浏览器支持,需要对迭代器进行 polyfill,从而使应用的执行速度稍慢,而下载速度稍大。
for..of
出于这个原因(并鼓励使用 and),一些前端风格指南完全禁止!
map
filter
for..of
但上述问题不适用于 Node.js 应用程序,现在这些应用程序得到了很好的支持。
for..of
就我个人而言,我倾向于使用任何看起来最容易阅读的内容,除非性能或缩小已成为主要问题。所以这些天我更喜欢使用 而不是 ,但我总是会在适用时使用 or or find
或一些
。
(为了我的同事,我很少使用reduce
。for..of
forEach()
map
filter
它是如何工作的?
for (var i = 0; i < array.length; i++) { ... } // Forwards
for (var i = array.length; i--; ) { ... } // Reverse
您会注意到这是中间子句(我们通常看到比较的地方),最后一个子句是空的(我们通常看到的地方)。这意味着这也被用作继续的条件。至关重要的是,在每次迭代之前都会执行和检查它。i--
i++
i--
它怎么能从
array.length
开始而不爆炸?由于在每次迭代之前运行,因此在第一次迭代中,我们实际上将访问该项目,以避免
Array-out-of-bounds项目出现任何问题。i--
array.length - 1
undefined
为什么在索引 0 之前不停止迭代?
当条件的计算结果为 falsey 值时(当它产生 0 时),循环将停止迭代。
i--
诀窍在于,与 不同的是,尾随运算符递减,但在递减之前产生值。您的主机可以演示这一点:
--i
i--
i
> var i = 5; [i, i--, i];
[5, 5, 4]
因此,在最后一次迭代中,我以前是 1,表达式将其更改为 0,但实际上产生 1(真实),因此条件通过。在下一次迭代中,将 i 更改为 -1,但产生 0 (falsey),导致执行立即从循环底部掉落。
i--
i--
在传统的 forward for 循环中,并且是可互换的(正如 Douglas Crockford 指出的那样)。然而,在反向 for 循环中,因为我们的递减也是我们的条件表达式,所以如果我们想在索引 0 处处理项目,我们必须坚持下去。
i++
++i
i--
琐事
有些人喜欢在反向循环中画一个小箭头,并以眨眼结束:for
for (var i = array.length; i --> 0 ;) {
感谢 WYL 向我展示了反向 for 循环的好处和恐怖之处。
我知道这是一篇旧文章,已经有很多很棒的答案了。为了更完整一点,我想我会使用 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
优点
- 易于阅读
- 易于书写
- 如果可用,将使用 ES5 forEach 循环。现在,我将讨论 cons 部分的效率,因为 forEach 循环比 for 循环慢得多。我之所以提到这一点,是因为保持一致和标准化是件好事。
angular.forEach
请考虑以下 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循环,并且很难阅读和编写。
缺点
- 效率。,就此而言,原生的循环都比正常循环慢得多......慢约 90%。因此,对于大型数据集,最好坚持使用本机循环。
angular.forEach
forEach
for
for
- 没有中断、继续或返回支持。 实际上由“意外”支持,继续在一个简单的函数中放置一个语句,这将导致它继续退出该迭代的函数。这也是因为原生也不支持中断或继续。
continue
angular.forEach
return;
angular.forEach(array, function(item) { if (someConditionIsTrue) return; });
forEach
我相信还有其他各种优点和缺点,请随时添加您认为合适的任何优点和缺点。我觉得,底线,如果你需要效率,坚持只使用原生循环来满足你的循环需求。但是,如果你的数据集较小,并且可以放弃一些效率以换取可读性和可写性,那么一定要把那个坏孩子扔进去。for
angular.forEach
没有内在的闯入能力.要中断执行,请使用以下方法:forEach
Array#some
[1,2,3].some(function(number) {
return number === 1;
});
这之所以有效,是因为一旦任何按数组顺序执行的回调返回 true,就会返回 true,从而缩短其余回调的执行。 原始答案 参见 数组原型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)
}
where 避免了与任何其他语言相关的奇怪现象,并使其像任何其他语言的循环一样工作,并在循环内绑定,而不是在函数内绑定。of
in
for
let
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 - 与 every 非常相似,只是 filter 返回一个数组,其中包含返回给定函数 true 的元素。
// 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 - 在条件通过时循环。这似乎是最快的循环
var text = "";
var i = 0;
while (i < 10) {
text += i + ") something<br>";
i++;
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>
do/while - 当条件为真时,也会遍历代码块,将至少运行一次
var text = ""
var i = 0;
do {
text += i + ") something <br>";
i++;
}
while (i < 10);
document.getElementById("example").innerHTML = text;
<p id="example"></p>
函数循环 - , , , also (它们遍历函数,但如果您需要对数组执行某些操作,则使用它们。forEach
map
filter
reduce
// 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>
有关数组函数式编程的更多信息和示例,请查看博客文章 JavaScript 中的函数式编程:映射、过滤和归约。
最接近您想法的方法是使用 which 接受闭包函数,该函数将为数组的每个元素执行。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);
}
如果你有一个庞大的数组,你应该使用迭代器
来获得一些效率。迭代器是某些 JavaScript 集合(如 Map
、Set
、String
、Array
)的属性。甚至,为了..在
引擎盖下使用迭代器
。
迭代器允许您一次使用列表中的一个项目,就好像它们是流一样,从而提高了效率。迭代器的特别之处在于它遍历集合的方式。其他循环需要预先加载整个集合才能对其进行迭代,而迭代器只需要知道集合中的当前位置。
您可以通过调用迭代器的方法访问当前项。下一个方法将返回当前项的值
和一个布尔
值,以指示何时到达集合的末尾。以下是从数组创建迭代器的示例。next
使用 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
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
你可以在这里阅读更多关于的信息。iteration protocol
您可以像这样调用 forEach:
forEach
将遍历您提供的数组,并且对于每次迭代,它将具有保存该迭代值的数组。如果需要索引,可以通过在 forEach 的回调函数中将 作为第二个参数传递来获取当前索引。element
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
}
总结:
在迭代数组时,我们通常希望实现以下目标之一:
我们想遍历数组并创建一个新数组:
Array.prototype.map
我们想遍历数组,而不是创建一个新数组:
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
返回一个新数组并使旧数组保持不变(除非您使用 think like 显式操作它)。map
forEach
for..of
splice
另请注意,函数的回调提供当前迭代的索引号作为第二个参数。此外,第三个参数是否提供调用的数组?有时,这些属性可能非常有用。map
map
循环使用forEach
forEach
是一个函数,它位于该函数上,该函数将回调函数作为参数。然后,它对数组中的每个元素执行此回调函数。与函数相反,forEach 函数不返回任何内容 ()。例如:Array.prototype
map()
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);
}
在上面的例子中,代表数组元素,是我们想要循环的数组。请注意,该名称是任意的,如果适用,我们可以选择任何其他名称,例如“el”或更具声明性的名称。element
arr
element
不要将循环与循环混淆。 将遍历数组的所有可枚举属性,而循环将仅遍历数组元素。例如: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 的新手来说,解构和使用扩展运算符非常有用,因为它更易于人类阅读/美观,尽管一些 JavaScript 老手可能会认为它很混乱。青少年或其他一些人可能会发现它很有用。
以下示例将使用
for...of
语句和.forEach
方法。示例 6、7 和 8 可以与任何功能循环一起使用,如 、 、 、 、 。有关这些方法的详细信息,请查看 Array 对象。
.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:使用 和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);
});
我来自Python,我发现这种方式更清晰。
theArray 是数组,并且是数组的元素: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);
});
但归根结底,两者都在做同样的事情。
评论
for (instance in theArray)
与 不同。前者遍历键,后者遍历值。所以如果然后会打印 , , 而 将打印 , , .for (let instance of theArray)
theArray = ["a", "b", "c"]
for..in
0
1
2
for..of
"a"
"b"
"c"
// 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);
这样,您将生成一个新数组以供将来操作,并将跳过任何不希望的副作用。
如果要使用 arrow 函数遍历对象数组:
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');
})
/* 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 v 79.0,Safari v13.0.4和Firefox v71.0(64位)上进行了测试 - 关于优化的结论(以及微优化,通常不值得将其引入代码,因为好处很小,但代码复杂性会增加)。
看起来传统 (Aa) 是在所有浏览器上编写快速代码的不错选择。
for i
其他解决方案,如(Ad),都在C组中。通常比 Aa 慢 2 - 10 倍(甚至更多),但对于小数组,为了提高代码清晰度,可以使用它。
for-of
缓存在 (Ab, Bb, Be) 中的数组长度的循环有时更快,有时则不然。编译器可能会自动检测这种情况并引入缓存。缓存版本和非缓存版本(Aa、Ba、Bd)之间的速度差异约为 ~1%,因此引入似乎是一种微优化。
n
n
循环从最后一个数组元素(Ac、Bc)开始的类似解决方案通常比正向解决方案慢 ~30% - 可能是 CPU 内存缓存的工作方式 - 正向内存读取更适合 CPU 缓存)。建议不要使用此类解决方案。
i--
详
在测试中,我们计算数组元素的总和。我对小数组(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>
跨浏览器结果
所有测试浏览器的结果
包含 10 个元素的数组
Chrome的结果。您可以在此处在计算机上执行测试。
包含 1,000,000 个元素的数组
Chrome的结果。您可以在此处在您的机器上执行测试
您可以使用:
ForEach
theArray.forEach(function (array, index) { console.log(index); console.log(array); });
为
for(var i=0; i<theArray.length; i++) { console.log(i) }
地图
theArray.map(x => console.log(x));
地图
theArray.filter(x => console.log(x));
还有很多其他的迭代。
我认为这是要走的路: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'
与 不同,它不会跳过数组中的“漏洞”。 是有效的 JavaScript,只有第二个元素是一个“洞”。该数组在功能上等同于 。
forEach()
for/of
const arr = ['a',, 'c']
['a', undefined, 'c']
您可以在这篇关于 for/of
vs forEach()
的博文中阅读更多内容。
根据新的更新功能 ECMAScript 6 (ES6) 和 ECMAScript 2015,您可以将以下选项与循环一起使用:
for 循环
for(var i = 0; i < 5; i++){
console.log(i);
}
// Output: 0,1,2,3,4
为。。。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
为。。。循环数
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
假设我们有一个主题数组:
let ddl = new Array();
if (subjects) {
subjects.forEach(function (s) {ddl.push({"id": s.id, "label": s.name});});
}
评论
为。。。的 |forEach |地图
使用现代 JavaScript 语法遍历数组
const fruits = ['🍎', '🍋', '🍌' ]
👉🏽 为。。。之
for (const fruit of fruits) {
console.log(fruit) // '🍎', '🍋', '🍌'
}
👉🏽 forEach
fruits.forEach(fruit => {
console.log(fruit) // '🍎', '🍋', '🍌'
})
👉🏽 地图
*与上述两个不同,map() 创建一个新数组,并希望您在每次迭代后返回一些东西。
fruits.map(fruit => fruit) // ['🍎', '🍋', '🍌' ]
🛑 重要提示:由于 map() 旨在在每次迭代时返回一个值,因此它是转换数组中元素的理想方法:
fruits.map(fruit => 'cool ' + fruit) // ['cool 🍎', 'cool 🍋', 'cool 🍌' ]
另一方面,对于...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...of
如上表所示,应在合适的地方使用。由于它支持异步函数,因此跳过了非数字属性,并防止因意外修改循环索引而弄乱循环。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...in
评论
我确实喜欢这个。
foreach
const arr = ["apple", "banana", "orange", "pear", "grape"];
arr.forEach(function(element) {
console.log(element);
});
为。。之
const arr = ["apple", "banana", "orange", "pear", "grape"];
for (const element of arr) {
console.log(element);
}
评论