每个在JavaScript中的数组?
如何使用JavaScript循环访问数组中的所有条目?
我认为这是这样的:
forEach(instance in theArray)
凡theArray
是我的阵列,但是这似乎是不正确的。
TL; DR
for-in
否则不要使用原装。 你最好的投注通常是
for-of
循环(仅限ES2015 +), Array#forEach
( spec
| MDN
)(或其亲属some
等)(仅限ES5 +), for
循环, for-in
有保障。 但还有很多东西需要探索,请继续阅读......
JavaScript具有强大的语义来循环数组和类似数组的对象。 我将答案分为两部分:真正数组的选项,以及类似数组的事物选项,如arguments
对象,其他可迭代对象(ES2015 +),DOM集合等等。
我会很快注意到,通过将ES2015转换为ES5,即使在ES5引擎上,您现在也可以使用ES2015选件。 搜索“ES2015 transpiling”/“ES6 transpiling”了解更多...
好的,让我们看看我们的选择:
对于实际数组
在ECMAScript 5(“ES5”)中有三个选项,这是目前最广泛支持的版本,并且即将在ECMAScript 2015(“ES2015”,“ES6”)中提供两个选项,即供应商正在开发的最新版JavaScript在支持:
forEach
和相关(ES5 +) for
循环 for-in
for-of
(隐式使用迭代器)(ES2015 +) 细节:
1.使用forEach
和相关的
如果您使用的环境支持ES5的Array
功能(直接或使用垫片),则可以使用新的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填充”)。
forEach
具有的优点是,您不必在包含范围中声明索引和值变量,因为它们作为迭代函数的参数提供,因此恰好适用于该迭代。
如果您担心为每个数组条目进行函数调用的运行时成本,请不要; 细节。
另外, forEach
是“通过它们全部循环”的功能,但是ES5定义了其他一些有用的“通过数组和功能工作”功能,其中包括:
every
(停止循环第一次迭代器返回false
或falsey) some
(第一次停止循环迭代器返回true
或true
东西) filter
(创建一个新的数组,包括过滤器函数返回true
元素并省略返回false
元素) map
(根据迭代器函数返回的值创建一个新数组) reduce
(通过重复调用迭代器来构建一个值,传入以前的值;请参阅规范以了解详细信息;用于汇总数组的内容和许多其他内容) reduceRight
(如reduce
,但以降序而不是升序排列) 2.使用一个简单的for
循环
有时旧的方式是最好的:
var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
console.log(a[index]);
}
如果数组的长度将不会在循环过程中改变,它在性能敏感的代码(不可能),一个稍微复杂一点的版本抓住了长度达阵可能是一点点更快:
var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
console.log(a[index]);
}
和/或倒计时:
var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
console.log(a[index]);
}
但是对于现代的JavaScript引擎,你很少需要排出最后一滴果汁。
在ES2015及更高版本中,您可以使您的索引和值变量局部用于for
循环:
let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
let value = a[index];
}
//console.log(index); // 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 => {
alert("Index is: " + index);
});
}
如果你有五个div,如果你点击第一个,你会得到“Index is:0”,如果你点击了最后一个,你会得到“Index is:4”。 如果你使用这个不起作用 var
,而不是let
。
3.正确使用for-in
你会让人们告诉你使用for-in
,但这不是for-in
用处。 for-in
循环通过对象的可枚举属性,而不是数组的索引。 订单不能保证 ,即使在ES2015(ES6)中也是如此。 ES2015确实定义了对象属性的顺序(通过[[OwnPropertyKeys]]
, [[Enumerate]]
以及使用它们的东西,如Object.getOwnPropertyKeys
),但它没有定义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 are explained
/^0$|^[1-9]d*$/.test(key) && // and then hidden
key <= 4294967294 // away below
) {
console.log(a[key]);
}
}
注意两个检查:
该对象具有自己的名称(不是从它的原型继承的),并且
该键是以普通字符串形式的基数为10的数字字符串,其值为<= 2 ^ 32 - 2(即4,294,967,294)。 这个数字从哪里来? 它是规范中数组索引定义的一部分。 其他数字(非整数,负数,大于2 ^ 32 - 2的数字)不是数组索引。 2 ^ 32 - 2的原因在于,它使最大索引值低于2 ^ 32 - 1 ,这是数组length
可以达到的最大值。 (例如,一个数组的长度适合32位无符号整数。)(支持RobG在我的博客文章中发表评论说我以前的测试不太正确。)
在大多数数组上,每循环迭代会增加一点额外开销,但是如果您有一个稀疏数组,它可能是一种更有效的循环方式,因为它只循环实际存在的条目。 例如,对于上面的数组,我们总共循环三次(对于键"0"
, "10"
和"10000"
- 请记住,它们是字符串),而不是10,001次。
现在,你不会每次写这个,所以你可以把它放在你的工具箱中:
function arrayHasOwnIndex(array, prop) {
return array.hasOwnProperty(prop) && /^0$|^[1-9]d*$/.test(prop) && prop <= 4294967294; // 2^32 - 2
}
然后我们会这样使用它:
for (key in a) {
if (arrayHasOwnIndex(a, key)) {
console.log(a[key]);
}
}
或者,如果你对“大多数情况下足够好”的测试感兴趣,你可以使用它,但是当它接近时,它不是很正确:
for (key in a) {
// "Good enough" for most cases
if (String(parseInt(key, 10)) === key && a.hasOwnProperty(key)) {
console.log(a[key]);
}
}
4.使用for-of
(隐式使用迭代器)(ES2015 +)
ES2015将迭代器添加到JavaScript中。 使用迭代器的最简单方法是新的for-of
语句。 它看起来像这样:
var val;
var a = ["a", "b", "c"];
for (val of a) {
console.log(val);
}
输出:
a b c
在封面下,从数组获取迭代器并循环遍历它,从中获取值。 这没有使用for-in
问题,因为它使用由对象(数组)定义的迭代器,并且数组定义迭代器迭代其条目(而不是它们的属性)。 与ES5中的for-in
不同for-in
条目访问的顺序是其索引的数字顺序。
5.明确使用迭代器(ES2015 +)
有时候,你可能想明确使用迭代器。 你也可以做到这一点,虽然它比原因for-of
笨得多。 它看起来像这样:
var a = ["a", "b", "c"];
var it = a.values();
var entry;
while (!(entry = it.next()).done) {
console.log(entry.value);
}
迭代器是一个函数(特别是一个生成器),每当你next
一次调用时都会返回一个新对象。 迭代器返回的对象具有一个属性done
,告诉我们它是否完成,并且具有该迭代值的属性value
。
value
的含义取决于迭代器; 数组支持(至少)三个返回迭代器的函数:
values()
:这是我在上面使用的那个。 它返回一个迭代器,其中每个value
都是该迭代的值。 keys()
:返回一个迭代器,其中每个value
都是该迭代的关键(对于我们的a
,这将是"0"
,然后是"1"
,然后是"2"
)。 entries()
:返回一个迭代器,其中每个value
都是该迭代形式为[key, value]
的数组。 (在撰写本文时,Firefox 29支持entries
和keys
但不支持values
。)
对于类似于数组的对象
除了真正的数组之外,还有类似数组的对象,它们具有length
属性和带有数字名称的属性: NodeList
实例, arguments
对象等。我们如何遍历它们的内容?
使用上面的任何选项作为数组
至少有一些,也可能是大部分甚至全部的数组方法经常适用于类似数组的对象:
使用forEach
和相关(ES5 +)
Array.prototype
上的各种函数是“有意通用的”,通常可以通过Function#call
或Function#apply
类似数组的对象上。 (请参阅此答案结尾处的主机提供的对象警告,但这是一个罕见的问题。)
假设你想在Node
的childNodes
属性上使用forEach
。 你会这样做:
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
将使用由对象提供的迭代器(如果有的话); 我们必须看看它如何与各种类似阵列的对象,尤其是主机提供的对象一起使用。
明确使用迭代器(ES2015 +)
见#4,我们将看看迭代器是如何发挥出来的。
创建一个真正的数组
其他时候,您可能需要将类似数组的对象转换为真数组。 这样做非常简单:
使用数组的slice
方法
我们可以使用数组的slice
方法,就像上面提到的其他方法是“有意通用的”,所以可以用于类似数组的对象,如下所示:
var trueArray = Array.prototype.slice.call(arrayLikeObject);
举例来说,如果我们想将一个NodeList
转换为一个真正的数组,我们可以这样做:
var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
请参阅下面的主持人提供的对象注意事项。 特别要注意的,这将在IE8失败和更早版本,这不是让你使用主机提供的对象作为this
类似。
使用传播符号( ...
)
也可以使用ES2015的扩展表示法(MDN目前称其为运营商;它不是一种),使用支持该功能的JavaScript引擎:
var trueArray = [...iterableObject];
例如,如果我们想用一个扩展的语法将一个NodeList
转换成一个真正的数组,那么这个变得非常简洁:
var divs = [...document.querySelectorAll("div")];
使用Array.from
(spec)| (MDN)
Array.from
(ES2015,但是shimmable)从一个类似数组的对象中创建一个数组,可选择首先通过映射函数传递条目。 所以:
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]]
操作的一个诚实的答案。 在撰写本文时,浏览器在这方面做得很好,但ES5规范确实允许主机提供的对象可能不诚实; 它在§8.6.2中(在该部分开头附近的大表格下面几段),其中说:
主机对象可以以任何方式实现这些内部方法,除非另有规定; 例如,一种可能性是特定主机对象的[[Get]]
和[[Put]]
确实获取并存储属性值,但[[HasProperty]]
总是生成false 。
(我找不到在ES2015规格相当于空话,但它势必仍是如此。)此外,由于这一在现代浏览器编写常见的主机提供阵列状物体( NodeList
的情况下,例如) 做正确处理[[HasProperty]]
,但测试很重要。
编辑 :这个答案是绝望的过时。 对于更现代的方法,请查看数组上可用的方法。 感兴趣的方法可能是:
在JavaScript中迭代一个数组的标准方法是一个for
-loop的vanilla:
var length = arr.length,
element = null;
for (var i = 0; i < length; i++) {
element = arr[i];
// Do something with element i.
}
但是,请注意,如果您有一个密集的数组,并且每个索引都被一个元素占据,那么这种方法才是有效的。 如果数组很稀疏,那么你可以用这种方法来解决性能问题,因为你会迭代很多在数组中并不存在的索引。 在这种情况下, for .. in
-loop可能是一个更好的主意。 但是 ,您必须使用适当的安全措施以确保只对数组(即数组元素)所需的属性起作用,因为for..in
-loop也将在旧版浏览器中枚举,或者如果额外属性被定义为enumerable
。
在ECMAScript 5中,数组原型上会有一个forEach方法,但在传统浏览器中不支持。 因此,为了能够始终如一地使用它,您必须拥有一个支持它的环境(例如,用于服务器端JavaScript的Node.js),或者使用“Polyfill”。 然而,这种功能的填充是微不足道的,因为它使代码更易于阅读,所以它是一个很好的填充。
如果您使用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].
}
链接地址: http://www.djcxy.com/p/189.html