如何使用JavaScript遍历数组中的所有条目?
我以为是这样的:
forEach(instance in theArray)
theArray
我的数组在哪里,但这似乎是不正确的。
如何使用JavaScript遍历数组中的所有条目?
我以为是这样的:
forEach(instance in theArray)
theArray
我的数组在哪里,但这似乎是不正确的。
Answers:
TL; DR
for-in
除非您将其与防护措施配合使用,或者至少知道它为什么会咬住您,否则请勿使用。通常最好的选择是
但是,还有很多更多的探索,阅读...
JavaScript具有强大的语义,可以遍历数组和类似数组的对象。我将答案分为两部分:真正数组的选项,以及仅是数组之类的东西的选项,例如arguments
对象,其他可迭代对象(ES2015 +),DOM集合等。
我会很快注意到,您现在可以通过将ES2015转换为ES5 来使用ES2015选项,甚至在ES5引擎上也可以使用。搜索“ ES2015 transpiling” /“ ES6 transpiling”以了解更多...
好吧,让我们看看我们的选择:
您目前在ECMAScript 5(“ ES5”)中拥有三个选项,这是目前最广泛支持的版本,在ECMAScript 2015中又添加了两个选项(“ ES2015”,“ ES6”):
forEach
及相关(ES5 +)for
循环for-in
for-of
(隐式使用迭代器)(ES2015 +)细节:
forEach
及相关在任何可以访问Array
ES5(直接或使用polyfills)添加的功能的模糊现代环境(因此,不是IE8)中,都可以使用forEach
(spec
| MDN
):
var a = ["a", "b", "c"];
a.forEach(function(entry) {
console.log(entry);
});
forEach
接受一个回调函数,以及(可选)一个this
在调用该回调时使用的值(上面未使用)。依次为数组中的每个条目调用回调,从而跳过稀疏数组中不存在的条目。尽管上面只使用了一个参数,但回调函数使用以下三个参数调用:每个条目的值,该条目的索引以及对要迭代的数组的引用(以防您的函数尚未使用它) )。
除非您支持IE8之类的过时浏览器(截至2016年9月,NetApps在该市场上所占份额刚刚超过4%),forEach
否则您可以在没有垫片的情况下愉快地在通用网页中使用。如果确实需要支持陈旧的浏览器,forEach
则填充/填充很容易完成(搜索“ es5 shim”以获得多个选项)。
forEach
这样做的好处是您不必在包含范围内声明索引和值变量,因为它们是作为迭代函数的参数提供的,因此可以很好地将作用域限定为该迭代。
如果您担心为每个数组条目进行函数调用的运行时成本,请不必担心;细节。
此外,forEach
它是“遍历所有对象”功能,但是ES5定义了其他几个有用的“遍历数组并做事”功能,包括:
every
(在第一次返回回调false
或出现错误时停止循环)some
(在第一次返回回调true
或发生错误时停止循环)filter
(创建一个新数组,其中包含过滤器函数返回的元素,true
并省略其返回的元素false
)map
(根据回调返回的值创建一个新数组)reduce
(通过重复调用回调并传入先前的值来建立值;有关详细信息,请参见规范;对汇总数组内容和许多其他内容很有用)reduceRight
(如reduce
,但按降序而不是升序工作)for
循环有时,旧方法是最好的:
var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
console.log(a[index]);
}
如果数组的长度将不会在循环过程中改变,它在性能敏感的代码(不可能),一个稍微复杂一点的版本抓住了长度达阵可能是一个很小的有点快:
var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
console.log(a[index]);
}
和/或倒数:
var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
console.log(a[index]);
}
但是,使用现代JavaScript引擎,很少需要消耗掉最后的能量。
在ES2015及更高版本中,可以使索引和值变量在for
循环本地:
let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
let value = a[index];
console.log(index, value);
}
//console.log(index); // would cause "ReferenceError: index is not defined"
//console.log(value); // would cause "ReferenceError: value is not defined"
而且,当您执行此操作时,不仅会为每个循环迭代创建一个,value
而且还会index
为每个循环迭代重新创建一个,这意味着在循环主体中创建的闭包将保留对为该特定迭代创建的index
(和value
)的引用:
let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
divs[index].addEventListener('click', e => {
console.log("Index is: " + index);
});
}
如果您有五个div,则单击第一个将获得“索引为:0”,如果单击最后一个则将为“索引为:4”。如果您使用代替,则此方法将无效。var
let
for-in
你会得到别人告诉你使用for-in
,但是这不是for-in
对。for-in
遍历对象的可枚举属性,而不是数组的索引。甚至在ES2015(ES6)中也不保证顺序。ES2015 +确实定义了对象属性的顺序(通过[[OwnPropertyKeys]]
,[[Enumerate]]
以及使用属性的对象,例如Object.getOwnPropertyKeys
),但并未定义for-in
遵循该顺序的对象;不过,ES2020做到了。(其他答案的详细信息。)
for-in
数组上唯一真正的用例是:
仅查看第一个示例:for-in
如果使用适当的保护措施,则可以用来访问那些稀疏数组元素:
// `a` is a sparse array
var key;
var a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (key in a) {
if (a.hasOwnProperty(key) && // These checks are
/^0$|^[1-9]\d*$/.test(key) && // explained
key <= 4294967294 // below
) {
console.log(a[key]);
}
}
请注意以下三个检查:
该对象具有该名称的自身属性(不是从其原型继承的属性),并且
该键是所有十进制数字(例如,正常的字符串形式,而不是科学计数法),并且
键的值强制为数字时<= 2 ^ 32-2(即4,294,967,294)。这个数字从哪里来?它是规范中数组索引定义的一部分。其他数字(非整数,负数,大于2 ^ 32-2的数字)不是数组索引。它的2 ^ 32的理由- 2是使得大于2 ^ 32下一个最大的索引值- 1,这是一个数组的最大值length
可以有。(例如,数组的长度适合32位无符号整数。)(向RobG表示支持,在我的博客文章的评论中指出我先前的测试不太正确。)
当然,您不会在内联代码中执行此操作。您将编写一个实用程序函数。也许:
for-of
(隐式使用迭代器)(ES2015 +)ES2015 在JavaScript中添加了迭代器。使用迭代器最简单的方法是new for-of
语句。看起来像这样:
const a = ["a", "b", "c"];
for (const val of a) {
console.log(val);
}
在幕后,它从数组中获取一个迭代器并循环遍历,从而从中获取值。这没有使用for-in
has 的问题,因为它使用了由对象(数组)定义的迭代器,并且数组定义了其迭代器遍历其条目(而不是其属性)。与for-in
ES5 不同,访问条目的顺序是其索引的数字顺序。
有时,您可能想显式使用迭代器 。您也可以这样做,尽管它比笨拙得多。看起来像这样:for-of
const a = ["a", "b", "c"];
const it = a.values();
let entry;
while (!(entry = it.next()).done) {
console.log(entry.value);
}
迭代器是与规范中的迭代器定义匹配的对象。每次调用时,其next
方法都会返回一个新的结果对象。结果对象具有属性,done
告诉我们是否完成操作,以及一个value
具有该迭代值的属性。(done
如果是false
,value
则为可选,如果是,则为可选undefined
。)
的含义value
取决于迭代器;数组至少支持三个返回迭代器的函数:
values()
:这是我上面使用的那个。它返回迭代,其中每个value
是用于该迭代阵列条目("a"
,"b"
,和"c"
在实施例更早)。keys()
:返回一个迭代器,其中每个迭代器value
都是该迭代的关键(因此对于我们a
上面的代码,将是"0"
,然后是"1"
,然后是"2"
)。entries()
:返回一个迭代器,其中每个迭代器value
都是[key, value]
该迭代形式的数组。除了真正的数组之外,还有一些类数组对象,它们具有一个length
或多个具有数字名称的属性:NodeList
实例,arguments
对象等。我们如何遍历其内容?
上面的至少一些(可能是大多数甚至全部)数组方法经常同样适用于类似数组的对象:
使用forEach
及相关(ES5 +)
的各种功能Array.prototype
都是“有意的通用”功能,通常可以通过Function#call
或在类似数组的对象上使用Function#apply
。(在此答案的末尾,请参阅警告,以了解主机提供的对象,但这是一个罕见的问题。)
假设您要forEach
在Node
的childNodes
属性上使用。您可以这样做:
Array.prototype.forEach.call(node.childNodes, function(child) {
// Do something with `child`
});
如果要执行很多操作,则可能需要将函数引用的副本复制到变量中以供重用,例如:
// (This is all presumably in some scoping function)
var forEach = Array.prototype.forEach;
// Then later...
forEach.call(node.childNodes, function(child) {
// Do something with `child`
});
使用一个简单的for
循环
显然,一个简单的for
循环适用于类似数组的对象。
正确使用for-in
for-in
具有与数组相同的保护措施,也应与类似数组的对象一起使用;上面#1中由主机提供的对象的警告可能适用。
使用for-of
(隐式使用迭代器)(ES2015 +)
for-of
使用对象提供的迭代器(如果有)。其中包括主机提供的对象。例如,NodeList
from 的规范querySelectorAll
已更新以支持迭代。HTMLCollection
from 的规格getElementsByTagName
不是。
明确使用迭代器(ES2015 +)
参见#4。
其他时候,您可能希望将类似数组的对象转换为真正的数组。做到这一点非常容易:
使用slice
数组的方法
我们可以使用slice
数组的方法,就像上面提到的其他方法一样,它是“故意通用的”,因此可以与类似数组的对象一起使用,如下所示:
var trueArray = Array.prototype.slice.call(arrayLikeObject);
因此,例如,如果我们要将a NodeList
转换为真实数组,则可以执行以下操作:
var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
请参阅下面的警告,了解主机提供的对象。特别要注意的是,这将在IE8及更早版本中失败,这不允许您this
像这样使用主机提供的对象。
使用传播语法(...
)
还可以将ES2015的扩展语法与支持该功能的JavaScript引擎一起使用。像一样for-of
,它使用对象提供的迭代器(请参阅上一节的#4):
var trueArray = [...iterableObject];
因此,例如,如果我们想将a NodeList
转换为真正的数组,使用扩展语法,这将变得非常简洁:
var divs = [...document.querySelectorAll("div")];
采用 Array.from
Array.from
(规格) | (MDN)(ES2015 +,但很容易填充),从类似数组的对象创建数组,可以选择先将条目通过映射函数传递。所以:
var divs = Array.from(document.querySelectorAll("div"));
或者,如果您想获取具有给定类的元素的标记名称的数组,则可以使用映射函数:
// Arrow function (ES2015):
var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
// Standard function (since `Array.from` can be shimmed):
var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
return element.tagName;
});
如果您将Array.prototype
函数与主机提供的类似数组的对象(DOM列表和浏览器而非JavaScript引擎提供的其他东西)一起使用,则需要确保在目标环境中进行测试,以确保主机提供的对象行为正常。大多数(现在)确实表现正常,但是测试很重要。原因是Array.prototype
您可能要使用的大多数方法都依赖于主机提供的对象,该对象为抽象[[HasProperty]]
操作提供了诚实的答案。在撰写本文时,浏览器在这方面做得很好,但是5.1规范确实允许由主机提供的对象可能是不诚实的。在§8.6.2中,该部分开头附近的大表下方的几段中),其中表示:
除非另有说明,否则宿主对象可以以任何方式实现这些内部方法。例如,一种可能性是,
[[Get]]
和[[Put]]
对特定宿主对象确实读取与存储的属性值但[[HasProperty]]
总是产生假。
(我在ES2015规范中找不到等效的用法,但情况肯定仍然如此。)同样,在撰写本文时,现代浏览器中的常见宿主提供的类似数组的对象[ NodeList
例如,实例] 确实可以处理[[HasProperty]]
正确,但是测试很重要。)
.forEach
不能有效地打破。您必须抛出异常才能执行中断。
some
。(我也希望允许打破forEach
,但他们,嗯,没有问我。;
,
after k=0
,而不是a ;
。记住,编程是很多事情,其中之一就是密切关注细节... :-)
length
不是方法)。:-)
注意:此答案已过时。对于更现代的方法,请查看array上可用的方法。感兴趣的方法可能是:
迭代的标准方式在阵列中的JavaScript是香草for
-loop:
var length = arr.length,
element = null;
for (var i = 0; i < length; i++) {
element = arr[i];
// Do something with element
}
但是请注意,这种方法仅在数组密集且每个索引都被一个元素占用的情况下才是好的。如果数组很稀疏,则使用这种方法会遇到性能问题,因为您将迭代很多数组中实际上不存在的索引。在这种情况下,for .. in
-loop可能是一个更好的主意。但是,您必须使用适当的保护措施以确保仅对数组的所需属性(即数组元素)起作用,因为for..in
-loop还将在旧版浏览器中枚举,或者如果其他属性定义为enumerable
。
在ECMAScript 5中,数组原型上将有一个forEach方法,但是旧版浏览器不支持该方法。因此,要能够始终如一地使用它,您必须具有一个支持它的环境(例如,服务器端JavaScript的Node.js),或使用“ Polyfill”。但是,用于此功能的Polyfill很简单,并且由于它使代码更易于阅读,因此可以很好地包含它。
for(instance in objArray)
使用不正确?对我来说,它看起来更简单,但我听到您说这不是正确的使用方式吗?
var
-keyword 声明多个变量。如果我们使用了分号,那么element
它将在全局范围内声明(或者说,JSHint在生产之前就对我们大喊大叫)。
如果您使用的是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
在每次迭代时都,这两者都可能需要一分钟的优化。array[i]
),那么前向循环将跳过该左移进入位置处的项目我,或再处理我这是个项右移。在传统的for循环中,您可以更新i以指向需要处理的下一个项目-1,但是简单地反转迭代方向通常是一种更简单,更优雅的解决方案。forEach()
for ... of
某些开发人员默认情况下使用反向for循环,除非有充分的理由进行向前循环。
尽管性能提升通常微不足道,但仍会引起尖叫:
“只需对列表中的每个商品都这样做,我不在乎订单!”
然而在实践中是不实际的意图的可靠指标,因为它是从这些场合没有区别,当你做对井井有条,真的需要循环反向。因此,实际上,将需要另一种构造来准确表达“无关紧要”的意图,这在包括ECMAScript在内的大多数语言中目前尚不可用,但是可以称为forEachUnordered()
。
如果顺序无关紧要,并且效率是一个问题(在游戏或动画引擎的最内层循环中),则可以将反向for循环用作您的首选模式。只要记住,在现有代码中看到反向for循环并不一定意味着顺序无关紧要!
通常,对于更关注透明度和安全性的高级代码,我以前建议将其Array::forEach
用作循环的默认模式(尽管这些天我更喜欢使用for..of
)。优先于forEach
反向循环的原因有:
for
和while
loop中的可能的惊喜)。然后,当您在代码中看到反向for循环时,这表明它是有充分原因(也许是上述原因之一)被反向的。看到传统的forward for循环可能表明可以进行移位。
(如果对您没有意义的讨论,那么您和您的代码可能会受益于观看Crockford关于“ 编程风格和您的大脑”的讲座。)
有一个关于是否辩论for..of
或者forEach()
是较好的:
为了最大程度地支持浏览器,for..of
需要为迭代器使用polyfill,从而使您的应用执行速度稍慢,下载速度稍大。
因此(并鼓励使用map
和filter
),一些前端样式指南for..of
完全禁止使用!
但是上述问题不适用于for..of
现在已得到很好支持的Node.js应用程序。
就个人而言,我倾向于使用最容易阅读的外观,除非性能或尺寸缩小已成为主要问题。因此,这些天我更喜欢使用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
从而避免了数组越界 undefined
项的任何问题。
为什么不停止在索引0之前进行迭代?
当条件i--
评估为假值时(当产生0时),循环将停止迭代。
诀窍在于,与不同--i
,尾随的i--
运算符递减,i
但在递减之前产生值。您的控制台可以证明这一点:
> var i = 5; [i, i--, i];
[5, 5, 4]
因此,在最后一次迭代中,我以前为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
循环结构完成的:
var index,
value;
for (index in obj) {
value = obj[index];
}
有一个陷阱。for..in
将遍历对象的每个可枚举成员,以及其原型上的成员。为了避免读取通过对象原型继承的值,只需检查属性是否属于对象:
for (i in obj) {
if (obj.hasOwnProperty(i)) {
//do stuff
}
}
此外,ECMAScript 5添加了一种forEach
方法Array.prototype
,可以使用回溯方法对数组进行枚举(polyfill在文档中,因此您仍然可以将其用于较旧的浏览器):
arr.forEach(function (val, index, theArray) {
//do stuff
});
重要的是要注意,Array.prototype.forEach
回调返回时不会中断false
。jQuery和Underscore.js提供了它们自己的变体,each
以提供可以短路的循环。
each
允许return false
用于打破循环的方法,但这forEach
不是一个选择。可以使用外部标志(即if (flag) return;
,但这只会阻止函数主体的其余部分执行,forEach
仍然会继续遍历整个集合。)
如果要遍历数组,请使用标准的三部分for
循环。
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
}
您可以通过myArray.length
向后缓存或对其进行迭代来获得一些性能优化。
length
。;)
var i, length, arrayItem;
在循环前声明以避免这种误解。
我知道这是一篇旧文章,并且已经有很多不错的答案。为了更加完整,我想我会使用AngularJS投入另一个。当然,这仅适用于您使用Angular的情况,很明显,尽管如此,我还是想把它放上去。
angular.forEach
接受2个参数和一个可选的第三个参数。第一个参数是要迭代的对象(数组),第二个参数是迭代器函数,可选的第三个参数是对象上下文(在循环内部基本称为“ this”)。
有多种方法可以使用forEach角度循环。最简单且可能最常用的是
var temp = [1, 2, 3];
angular.forEach(temp, function(item) {
//item will be each element in the array
//do something
});
将项目从一个数组复制到另一个数组的另一种有用方法是
var temp = [1, 2, 3];
var temp2 = [];
angular.forEach(temp, function(item) {
this.push(item); //"this" refers to the array passed into the optional third parameter so, in this case, temp2.
}, temp2);
不过,您不必这样做,只需执行以下操作即可,它等效于前面的示例:
angular.forEach(temp, function(item) {
temp2.push(item);
});
现在与使用angular.forEach
内置的香草味for
循环相反,使用该功能有优缺点。
优点
angular.forEach
将使用ES5 forEach循环。现在,我会去的利弊部分efficientcy,作为foreach循环是多比for循环更慢。我将其作为专业人士提及是因为保持一致和标准化非常好。考虑以下两个嵌套循环,它们执行的功能完全相同。假设我们有2个对象数组,每个对象包含一个结果数组,每个结果都有一个Value属性,该属性是一个字符串(或其他类型)。假设我们需要遍历每个结果,如果结果相等,则执行一些操作:
angular.forEach(obj1.results, function(result1) {
angular.forEach(obj2.results, function(result2) {
if (result1.Value === result2.Value) {
//do something
}
});
});
//exact same with a for loop
for (var i = 0; i < obj1.results.length; i++) {
for (var j = 0; j < obj2.results.length; j++) {
if (obj1.results[i].Value === obj2.results[j].Value) {
//do something
}
}
}
诚然,这是一个非常简单的假设的例子,但我已经写了三重嵌入使用第二种方法循环,这是非常难以阅读,并为此事写。
缺点
angular.forEach
和原生forEach
,对于这个问题,都这么多比正常情况下慢for
循环....约90%的速度较慢。因此,对于大型数据集,最好坚持使用本机for
循环。continue
实际上是由“ 意外 ” 支持的,要在一个angular.forEach
简单的return;
语句中继续在函数中放置一条语句,就像这样angular.forEach(array, function(item) { if (someConditionIsTrue) return; });
,它将导致该语句在该迭代中继续从函数中退出。这也是由于本机forEach
既不支持break也不继续的事实。我敢肯定,还有其他各种利弊,请随时添加您认为合适的任何内容。我认为,最重要的是,如果您需要效率,请坚持使用本机for
循环来满足您的循环需求。但是,如果您的数据集较小,并且为了交换可读性和可写性而放弃某种效率是可以的,那么一定要丢掉angular.forEach
那个坏孩子。
如果您不介意清空数组:
var x;
while(x = y.pop()){
alert(x); //do something
}
x
将包含的最后一个值,y
并将其从数组中删除。您还可以使用来shift()
从中提供和删除第一项y
。
[1, 2, undefined, 3]
。
[1, 2, 0, 3]
或[true, true, false, true]
一个forEach实现(请参阅jsFiddle):
function forEach(list,callback) {
var length = list.length;
for (var n = 0; n < length; n++) {
callback.call(list[n]);
}
}
var myArray = ['hello','world'];
forEach(
myArray,
function(){
alert(this); // do something
}
);
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库。它提供了许多有用的工具,例如each
和将自动将作业委派给本机(forEach
如果可用)。
var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});
Array.prototype.forEach()
。for each (variable in object)
为ECMA-357(EAX)标准的一部分已弃用。for (variable of object)
作为Harmony(ECMAScript 6)提案的一部分进行迭代的另一种方法。本for each
机JavaScript中没有任何循环。您可以使用库来获得此功能(我建议Underscore.js),使用简单的for
in循环。
for (var instance in objects) {
...
}
但是,请注意,可能有理由使用更简单的for
循环(请参阅堆栈溢出问题,为什么在数组迭代中使用“ for…in”是个坏主意?)
var instance;
for (var i=0; i < objects.length; i++) {
var instance = objects[i];
...
}
这是非稀疏列表的迭代器,其中索引从0开始,这是处理document.getElementsByTagName或document.querySelectorAll时的典型方案。
function each( fn, data ) {
if(typeof fn == 'string')
eval('fn = function(data, i){' + fn + '}');
for(var i=0, L=this.length; i < L; i++)
fn.call( this[i], data, i );
return this;
}
Array.prototype.each = each;
用法示例:
例子1
var arr = [];
[1, 2, 3].each( function(a){ a.push( this * this}, arr);
arr = [1, 4, 9]
范例#2
each.call(document.getElementsByTagName('p'), "this.className = data;",'blue');
每个p标签得到 class="blue"
例子#3
each.call(document.getElementsByTagName('p'),
"if( i % 2 == 0) this.className = data;",
'red'
);
其他p标签class="red"
>
例子#4
each.call(document.querySelectorAll('p.blue'),
function(newClass, i) {
if( i < 20 )
this.className = newClass;
}, 'green'
);
最后,前20个蓝色p标签更改为绿色
使用字符串作为函数时的警告:该函数是在上下文之外创建的,仅应在确定变量作用域的情况下使用。否则,最好传递范围更直观的功能。
有几种方法可以遍历JavaScript中的数组,如下所示:
因为 -这是最常见的一种。完整的代码块用于循环
var languages = ["Java", "JavaScript", "C#", "Python"];
var i, len, text;
for (i = 0, len = languages.length, text = ""; i < len; i++) {
text += languages[i] + "<br>";
}
document.getElementById("example").innerHTML = text;
<p 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-在条件为true的情况下也循环遍历代码块,至少运行一次
var text = ""
var i = 0;
do {
text += i + ") something <br>";
i++;
}
while (i < 10);
document.getElementById("example").innerHTML = text;
<p id="example"></p>
功能循环 - ,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中的函数编程:map,filter和reduce。
forEach
这不是“功能”循环,因为它不返回新值Array
(实际上它不返回任何东西),它只是进行迭代。
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
以防万一,对使用某些内置功能的阵列操作更感兴趣。
地图 -它创建一个回调函数的结果的新数组。需要格式化数组元素时,可以使用此方法。
// 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
每 -返回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。
// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
return (elem % 2 == 0)
});
// Output: [2,4,6]
我还想将其添加为反向循环的组成部分,以及上面也为希望使用此语法的人提供的答案。
var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
console.log(item);
}
优点:
这样做的好处是:您已经在第一个引用中已经有了引用,以后无需在另一行声明它。在对象数组中循环时很方便。
缺点:
每当引用为假-假(未定义等)时,这都会中断。但是,它可以用作优势。但是,这会使阅读起来有些困难。而且,根据浏览器的不同,可以对其进行“非”优化,使其工作速度比原始浏览器快。
已证明,对ECMAScript 6的新手来说,散布运算符的解构和使用非常有用,因为它更易于阅读/美观,尽管有些JavaScript老手可能认为它很杂乱。青少年或其他人可能会发现它很有用。
以下示例将使用该
for...of
语句和.forEach
方法。实施例6,7,和8可以与像任何官能环路被使用
.map
,.filter
,.reduce
,.sort
,.every
,.some
。有关这些方法的更多信息,请查看Array Object。
示例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);
});
最接近您的想法的方法是使用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]
map
不会使数组的元素发生变化,因为它会返回一个新数组,而该新数组的项是对该函数应用到旧数组项的结果。
您可以这样调用forEach:
forEach
将遍历您提供的数组,并且对于每次迭代,它将拥有element
该迭代的值。如果您需要索引,则可以通过传递i
for作为每个回调函数的第二个参数。
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
}
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);
});
如果您有大量阵列,则应使用它iterators
来提高效率。迭代器是一定的JavaScript集合的性质(如Map
,Set
,String
,Array
)。甚至,for..of
使用iterator
在引擎罩。
迭代器可让您一次将列表中的项目当作流消费,从而提高了效率。使迭代器与众不同的是它如何遍历集合。其他循环需要预先加载整个集合以便对其进行迭代,而迭代器仅需要知道集合中的当前位置。
您可以通过调用迭代器的next
方法来访问当前项目。下一个方法将返回value
当前项目的和,boolean
以指示您何时到达集合的末尾。以下是从数组创建迭代器的示例。
使用以下values()
方法将常规数组转换为迭代器:
const myArr = [2,3,4]
let it = myArr.values();
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
您还可以使用以下方式将常规数组转换为迭代器Symbol.iterator
:
const myArr = [2,3,4]
let it = myArr[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
您还可以将常规array
转换成iterator
这样:
let myArr = [8, 10, 12];
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
};
};
var it = makeIterator(myArr);
console.log(it.next().value); // {value: 8, done: false}
console.log(it.next().value); // {value: 10, done: false}
console.log(it.next().value); // {value: 12, done: false}
console.log(it.next().value); // {value: undefined, done: true}
注意:
iterable
默认情况下,对象不是。for..in
在这种情况下使用,因为它不是键值而是键。您可以iteration protocol
在此处了解更多信息。
如果要使用forEach()
,它将看起来像-
theArray.forEach ( element => {
console.log(element);
});
如果要使用for()
,它将看起来像-
for(let idx = 0; idx < theArray.length; idx++){
let element = theArray[idx];
console.log(element);
}
根据新的更新功能ECMAScript 6(ES6)和ECMAScript 2015,您可以在循环中使用以下选项:
for循环
for(var i = 0; i < 5; i++){
console.log(i);
}
// Output: 0,1,2,3,4
for ...在循环中
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
今天(二○一九年十二月一十八日)我对我进行测试的MacOS v10.13.6(高塞拉利昂),在Chrome v 79.0,Safari浏览器v13.0.4和Firefox v71.0(64位) -关于优化结论(和微优化其通常不值得将其介绍给代码,因为这样做的好处很小,但是代码复杂度却在增加。
看起来传统for i
(Aa)是在所有浏览器上编写快速代码的不错选择。
C组中的所有其他解决方案,例如for-of
(Ad),通常比Aa慢2到10倍(甚至更多),但是对于较小的数组,可以使用它-为了提高代码的清晰度。
缓存在n
(Ab,Bb,Be)中的数组长度的循环有时会更快,有时却不会。编译器可能会自动检测到这种情况并引入缓存。缓存版本和非缓存版本(Aa,Ba,Bd)之间的速度差异约为1%,因此,引入似乎n
是一种微优化。
i--
循环从最后一个数组元素(Ac,Bc)开始的类似解决方案通常比正向解决方案慢30%-可能是因为CPU内存缓存的工作方式 -前向存储器读取对于CPU缓存更为理想。建议不要使用此类解决方案。
在测试中,我们计算数组元素的总和。我对小型数组(10个元素)和大型数组(1M元素)进行测试,并将它们分为三组:
for
测试while
测试跨浏览器结果
所有经过测试的浏览器的结果
包含10个元素的数组
Chrome的搜索结果。您可以在此处在机器上执行测试。
具有1,000,000个元素的数组
Chrome的搜索结果。您可以在这里在机器上执行测试
在数组上进行迭代时,我们通常希望实现以下目标之一:
我们要遍历数组并创建一个新数组:
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
和另一个循环机制(例如forEach
和for..of
循环)之间的显着区别是,它map
返回一个新数组,并保持旧数组不变(除非您使用thinks那样显式地操作它splice
)。
另外,请注意,map
函数的回调提供当前迭代的索引号作为第二个参数。此外,第三个参数是否提供了map
被调用的数组?有时,这些属性可能非常有用。
forEach
forEach
是一个Array.prototype
以回调函数作为参数的函数。然后,它将对数组中的每个元素执行此回调函数。与该map()
函数相比,forEach函数不返回任何内容(undefined
)。例如:
let arr = [1, 2, 3, 4, 5];
arr.forEach((element, index, array) => {
console.log(element * 2);
if (index === 4) {
console.log(array)
}
// index, and oldArray are provided as 2nd and 3th argument by the callback
})
console.log(arr);
就像map
函数一样,forEach
回调函数将当前迭代的索引号作为第二个参数提供。另外,第三个参数是否提供了forEach
被调用的数组?
for..of
所述for..of
的循环遍历的阵列(或任何其他迭代的对象)的每一个元素。它以以下方式工作:
let arr = [1, 2, 3, 4, 5];
for(let element of arr) {
console.log(element * 2);
}
在上面的示例中,element
代表一个数组元素,arr
是我们要循环的数组。注意名称element
是任意的,并且在适用时我们可以选择其他任何名称,例如'el'或更具声明性的名称。
不要将for..in
循环与for..of
循环混淆。for..in
将循环遍历数组的所有可枚举属性,而for..of
循环仅遍历数组元素。例如:
let arr = [1, 2, 3, 4, 5];
arr.foo = 'foo';
for(let element of arr) {
console.log(element);
}
for(let element in arr) {
console.log(element);
}
forEach
不仅在寻找for
。