本文共 26543 字,大约阅读时间需要 88 分钟。
如何使用遍历数组中的所有条目?
我以为是这样的:
(instance in theArray)
其中theArray
是我的数组,但这似乎是不正确的。
如果您不介意清空数组:
var x;while(x = y.pop()){ alert(x); //do something }
x
将包含y
的最后一个值,并将其从数组中删除。 您还可以使用shift()
来从y
删除第一项。
一个forEach实现( ):
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 });
现在,一个简单的解决方案是使用 。 它提供了许多有用的工具,例如each
并且会自动将作业委派给本地forEach
如果有)。
如何工作的是:
var arr = ["elemA", "elemB", "elemC"];_.each(arr, function(elem, index, ar){...});
for each (variable in object)
已被弃用为ECMA-357( )标准的一部分。 for (variable of object)
作为Harmony(ECMAScript 6)提议的一部分进行迭代的另一种方法。 可能for(i = 0; i < array.length; i++)
循环不是最佳选择。 为什么? 如果您有这个:
var array = new Array();array[1] = "Hello";array[7] = "World";array[11] = "!";
该方法将从array[0]
调用到array[2]
。 首先,这将首先引用您甚至没有的变量,其次,您将在数组中没有变量,并且第三,这将使代码更粗体。 看这里,这就是我用的:
for(var i in array){ var el = array[i]; //If you want 'i' to be INT just put parseInt(i) //Do something with el}
如果希望它成为函数,则可以执行以下操作:
function foreach(array, call){ for(var i in array){ call(array[i]); }}
如果您想打破,更多的逻辑:
function foreach(array, call){ for(var i in array){ if(call(array[i]) == false){ break; } }}
例:
foreach(array, function(el){ if(el != "!"){ console.log(el); } else { console.log(el+"!!"); }});
它返回:
//Hello//World//!!!
有foreach
三种实现,如下所示。
var a = [3,2];$(a).each(function(){console.log(this.valueOf())}); //Method 1$.each(a, function(){console.log(this.valueOf())}); //Method 2$.each($(a), function(){console.log(this.valueOf())}); //Method 3
这是非稀疏列表的迭代器,其中索引从0开始,这是处理document.getElementsByTagName或document.querySelectorAll时的典型情况。
function each( fn, data ) { if(typeof fn == 'string') eval('fn = function(data, i){' + fn + '}'); for(var i=0, L=this.length; i < L; i++) fn.call( this[i], data, i ); return this;}Array.prototype.each = each;
用法示例:
例子1
var arr = [];[1, 2, 3].each( function(a){ a.push( this * this}, arr);arr = [1, 4, 9]
范例#2
each.call(document.getElementsByTagName('p'), "this.className = data;",'blue');
每个p标签都获得class="blue"
例子#3
each.call(document.getElementsByTagName('p'), "if( i % 2 == 0) this.className = data;", 'red');
每隔p个标签获得class="red"
>
例子#4
each.call(document.querySelectorAll('p.blue'), function(newClass, i) { if( i < 20 ) this.className = newClass; }, 'green');
最后,前20个蓝色p标签更改为绿色
使用字符串作为函数时的警告:该函数是在上下文之外创建的,仅应在确定变量作用域的情况下使用。 否则,最好传递范围更直观的功能。
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
进行比较,这两者都可能需要一分钟的优化。 array[i]
处删除或插入项目),则正向循环将跳过向左移到位置i的项目 ,或重新处理i右移的项目。 在传统的for循环中,您可以更新i以指向需要处理的下一个项目-1,但是简单地反转迭代方向通常是一种更简单 , 。 for ... of
forEach()
和ES6而输for ... of
。 某些开发人员默认情况下使用反向for循环,除非有充分的理由进行向前循环。
尽管性能提升通常微不足道,但仍会引起尖叫:
“只需对列表中的每个商品都这样做,我不在乎订单!”
然而在实践中,实际上并不是意图可靠的指示,因为它是从这些场合没有区别,当你做对的井井有条,而且确实需要循环反向。 因此,实际上,将需要另一种构造来准确表达“不在乎”的意图,这在大多数语言(包括ECMAScript)中目前尚不可用,但是可以将其称为例如forEachUnordered()
。
如果顺序无关紧要,并且效率是一个问题(在游戏或动画引擎的最内部循环中),则可以将反向for循环用作您的首选模式。 只要记住,在现有代码中看到反向for循环并不一定意味着顺序无关紧要!
通常,对于更关注透明度和安全性的高级代码,我以前建议使用作为循环的默认模式(尽管这些天我更喜欢使用for..of
)。 选择forEach
而不是反向循环的原因是:
for
和while
循环)。 然后,当您在代码中确实看到反向for循环时,这表明它是有充分原因(也许是上述原因之一)被反向的。 看到传统的forward for循环可能表明可以进行移位。
(如果对您没有意义的讨论,那么您和您的代码可能会受益于观看Crockford关于“ 的讲座。)
关于for..of
或forEach()
是否for..of
存在争议:
为了获得最大程度的浏览器支持, for..of
为迭代器使用for..of
,这会使您的应用执行速度稍慢,下载速度稍大。
因此(并鼓励使用map
和filter
), 完全禁止使用for..of
!
但是上述问题不适用于Node.js应用程序,因为for..of
现在得到了很好的支持。
而且此外, await
在forEach()
内部 。 在这种情况下,使用for..of
是 。
就个人而言,我倾向于使用最容易阅读的外观,除非性能或尺寸缩小已成为主要问题。 因此,这些天我更喜欢使用for..of
而不是forEach()
,但是在适用时,我将始终使用map
或filter
或或 。 (为了我的同事,我很少使用 。)
for (var i = 0; i < array.length; i++) { ... } // Forwardsfor (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 (假),从而导致执行立即退出循环的底部。
在传统的循环转发中, i++
和++i
是可互换的(正如Douglas Crockford指出的那样)。 但是在相反的for循环中,因为我们的减量也是我们的条件表达式,所以如果要处理索引0处的项,则必须坚持使用i--
。
有些人喜欢在反面for
循环画一个小箭头,并以眨眼结束:
for (var i = array.length; i --> 0 ;) {
感谢WYL向我展示反向for循环的好处和恐惧。
我知道这是一篇旧文章,并且已经有很多不错的答案。 为了更加完整,我想我会使用投入另一个。 当然,这仅适用于您使用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 loopfor (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
循环....约 。 因此,对于大型数据集,最好坚持使用本机的for
循环。 continue
实际上是由“支持 ”,继续在angular.forEach
你简单的把一个return;
函数中的语句,例如angular.forEach(array, function(item) { if (someConditionIsTrue) return; });
这将导致它继续执行该迭代的功能。 这也是由于本机forEach
不支持break或forEach
的事实。 我敢肯定,还有其他各种利弊,请随时添加您认为合适的任何内容。 我认为,最重要的是,如果您需要效率,请坚持使用本机for
循环来满足您的循环需求。 但是,如果您的数据集较小,并且为了交换可读性和可写性而放弃某种效率是可以的,那么angular.forEach
那个坏男孩扔个angular.forEach
。
没有内置的能力闯入forEach
。 要中断执行,请使用Array#some
如下所示:
[1,2,3].some(function(number) { return number === 1;});
之所以可行,是因为一旦按数组顺序执行的任何回调都返回true,则some
返回true,从而使其余的执行短路。 参见数组原型
我也想将其添加为反向循环的组成部分,并在上面为也希望使用此语法的人提供答案。
var foo = [object,object,object];for (var i = foo.length, item; item = foo[--i];) { console.log(item);}
优点:
这样做的好处是:您已经在第一个引用中已经有了引用,以后无需在另一行声明它。 在对象数组中循环时很方便。
缺点:
每当引用为假-假(未定义等)时,这都会中断。 但是,它可以用作优势。 但是,这会使阅读起来有些困难。 并且还可以根据浏览器进行“非”优化,以使其比原始浏览器更快地工作。
从ECMAScript 6开始:
list = [0, 1, 2, 3] for (let obj of list) { console.log(obj) }
其中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
以防万一,对使用某些内置功能的阵列操作更感兴趣。
地图 -它创建一个回调函数的结果的新数组。 需要格式化数组元素时,可以使用此方法。
// 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 : 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]
有几种方法可以遍历JavaScript中的数组,如下所示:
因为 -这是最常见的一种 。 完整的代码块用于循环
var languages = ["Java", "JavaScript", "C#", "Python"]; var i, len, text; for (i = 0, len = languages.length, text = ""; i < len; i++) { text += languages[i] + ""; } document.getElementById("example").innerHTML = text;
while-在条件通过时循环。 这似乎是最快的循环
var text = ""; var i = 0; while (i < 10) { text += i + ") something"; i++; } document.getElementById("example").innerHTML = text;
do / while-在条件为true的情况下也循环遍历代码块,至少运行一次
var text = "" var i = 0; do { text += i + ") something "; i++; } while (i < 10); document.getElementById("example").innerHTML = text;
功能循环 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});
有关数组上函数编程的更多信息和示例,请参阅博客文章的 。
与您的想法最接近的一种方法是使用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文件,则应该反转index和value参数的位置
$("#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集合(例如 , , , )的属性。 甚至在使用iterator
。
迭代器可让您一次将列表中的项目当作流消费,从而提高了效率。 使迭代器与众不同的是它如何遍历集合。 其他循环需要预先加载整个集合以便对其进行迭代,而迭代器仅需要知道集合中的当前位置。
您可以通过调用迭代器的next
方法访问当前项。 下一个方法将返回当前项目的value
和一个boolean
以指示您何时到达集合的末尾。 以下是从数组创建迭代器的示例。
使用方法将常规数组转换为迭代器,如下所示:
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());
您还可以使用将常规数组转换为迭代器,如下所示:
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:
let Array = [1,3,2];theArray.forEach((element) => { // Use the element of the array console.log(element)}
element
将具有从0到数组长度的每个索引的值。
输出:
132
说明:
forEach
在原型类中。 您也可以将其称为theArray.prototype.forEach(...);
原型:
您也可以像这样遍历数组:
for (let i=0; i
在数组上进行迭代时,我们通常希望实现以下目标之一:
我们要遍历数组并创建新数组:
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
以新数组的形式返回,而使旧数组保持完整 (除非您使用诸如splice
类的方式显式操作了它)。
还要注意, map
函数的回调提供了当前迭代的索引号作为第二个参数。 此外,第三个参数是否提供了在其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
是我们要循环的数组。 请注意,name element
是任意的,并且在适用时我们可以选择其他任何名称,例如'el'或更具声明性的名称。
不要将for..in
循环与for..of
循环混淆。 for..in
将循环for..in
数组的所有可枚举属性,而for..of
循环将仅遍历数组元素。 例如:
let arr = [1, 2, 3, 4, 5]; arr.foo = 'foo'; for(let element of arr) { console.log(element); } for(let element in arr) { console.log(element); }
在ECMAScript 6 和使用循环
事实证明,对ECMAScript 6的新手来说,散布运算符的解构和使用非常有用,因为它更易于阅读/美观,尽管有些JavaScript老手可能认为它很杂乱。 青少年或其他人可能会发现它很有用。
以下示例将使用语句和方法。
实施例6,图7和8可以与像任何官能循环使用
.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[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
一起.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
一起.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
一起.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); });
// Looping through using the foreach ECMAScript 6 way var data = new Array(1, 2, 3, 4, 5); data.forEach((val,index) => { console.log("index: ", index); // Index console.log("value: ", val); // Value });
如果要保持功能性代码,请使用map
:
theArray.map(instance => do_something);
这样,您将为以后的操作生成一个新的数组,并且将跳过任何不希望的副作用。
如果要使用箭头功能遍历对象数组:
let arr = [{name:'john', age:50}, {name:'clark', age:19}, {name:'mohan', age:26}]; arr.forEach((person)=>{ console.log('I am ' + person.name + ' and I am ' + person.age + ' old'); })
TL; DR
for-in
防护措施,否则至少不要知道为什么它会咬您。 通常最好的选择是
for-of
循环(仅for-of
ES2015 +), Array#forEach
( | )(或其some
类似的亲戚)(仅适用于ES5 +), for
循环, for-in
有保障。 但是,还有其它更多的探索,阅读...
JavaScript具有强大的语义,可以遍历数组和类似数组的对象。 我将答案分为两部分:真正数组的选项,以及仅是数组之类的东西的选项,例如arguments
对象,其他可迭代对象(ES2015 +),DOM集合,等等。
我会很快注意到,您现在可以通过将ES2015转换为ES5,甚至在ES5引擎上使用ES2015选项。 搜索“ ES2015 transpiling” /“ ES6 transpiling”以了解更多...
好吧,让我们看看我们的选择:
您目前在 (“ ES5”)中拥有三个选项,这是目前最广泛支持的版本,在又添加了两个选项(“ ES2015”,“ ES6”):
forEach
及相关(ES5 +) for
循环 for-in
for-of
(隐式使用迭代器)(ES2015 +) 细节:
forEach
及相关 在任何可以访问由ES5添加的Array
功能(直接或使用polyfills)的模糊现代环境(因此,不是IE8)中,都可以使用forEach
( | ):
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定义了其他一些有用的“遍历数组并执行操作”功能,包括:
false
或false
时停止循环) true
或true
时停止循环) true
元素,并忽略其中返回false
元素) 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及更高版本中,可以使index和value变量位于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"
let a = ["a", "b", "c"]; for (let index = 0; index < a.length; ++index) { let value = a[index]; console.log(index, value); } try { console.log(index); } catch (e) { console.error(e); // "ReferenceError: index is not defined" } try { console.log(value); } catch (e) { console.error(e); // "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); });}
let divs = document.querySelectorAll("div"); for (let index = 0; index < divs.length; ++index) { divs[index].addEventListener('click', e => { console.log("Index is: " + index); }); }
zeroonetwothreefour
如果您有五个div,则单击第一个将获得“索引为:0”,如果单击最后一个则将为“索引为:4”。 如果你使用这个不起作用 var
,而不是let
。
for-in
您会看到有人告诉您使用for-in
,但这 。 for-in
遍历对象的可枚举属性 ,而不是数组的索引。 甚至在ES2015(ES6)中也不保证顺序 。 ES2015 +确实定义了对象属性的顺序(通过 , 以及使用它们的对象,如 ),但并未定义for-in
将遵循该顺序。 ( 详细信息。)
数组上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表示支持,在的评论中指出我先前的测试不太正确。)
当然,您不会在内联代码中执行此操作。 您将编写一个实用程序函数。 也许:
// Utility function for antiquated environments without `forEach` var hasOwn = Object.prototype.hasOwnProperty; var rexNum = /^0$|^[1-9]\\d*$/; function sparseEach(array, callback, thisArg) { var index; for (var key in array) { index = +key; if (hasOwn.call(a, key) && rexNum.test(key) && index <= 4294967294 ) { callback.call(thisArg, array[key], index, array); } } } var a = []; a[5] = "five"; a[10] = "ten"; a[100000] = "one hundred thousand"; ab = "bee"; sparseEach(a, function(value, index) { console.log("Value at " + index + " is " + value); });
for-of
(隐式使用迭代器)(ES2015 +) ES2015将迭代器添加到JavaScript。 使用迭代器的最简单方法是新的for-of
语句。 看起来像这样:
const a = ["a", "b", "c"]; for (const val of a) { console.log(val); }
在幕后,它从数组中获取一个迭代器并循环遍历,从而从中获取值。 这不存在使用for-in
问题,因为它使用由对象(数组)定义的迭代器,并且数组定义其迭代器通过其项 (而不是其属性)进行迭代。 与ES5中的for-in
不同for-in
访问条目的顺序是其索引的数字顺序。
有时,您可能想显式使用迭代器。 您也可以这样做,尽管它比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
。 (如果为false
,则done
是可选的;如果undefined
,则value
是可选的。)
value
的含义因迭代器而异; 数组至少支持三个返回迭代器的函数:
values()
:这是我上面使用的那个。 它返回一个迭代器,其中每个value
都是该迭代的数组条目(在前面的示例中为"a"
, "b"
和"c"
)。 keys()
:返回一个迭代器,其中每个value
都是该迭代的键(因此对于我们上面的a
,它将是"0"
,然后是"1"
,然后是"2"
)。 entries()
:返回一个迭代器,其中每个value
都是该迭代形式为[key, value]
形式的数组。 除了真正的数组之外,还有一些类似数组的对象,它们具有length
属性和带有数字名称的属性: NodeList
实例, arguments
对象等。我们如何遍历它们的内容?
上面的至少一些(可能是大多数甚至全部)数组方法经常同样适用于类似数组的对象:
适用于forEach
及相关(ES5 +)
Array.prototype
上的各种函数是“有意通用的”,通常可以通过或在类似数组的对象上 。 (在此答案的末尾,请参阅警告,以了解主机提供的对象 ,但这是一个罕见的问题。)
假设您要在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
将使用对象提供的迭代器(如果有); 我们将不得不看一下它如何与各种类似数组的对象一起运行,尤其是主机提供的对象。 例如,来自querySelectorAll
的NodeList
规范已更新为支持迭代。 来自getElementsByTagName
的HTMLCollection
规范不是。
明确使用迭代器(ES2015 +)
参见#4,我们必须看看迭代器如何发挥作用。
其他时候,您可能希望将类似数组的对象转换为真正的数组。 做到这一点非常容易:
使用数组的方法
我们可以使用数组的slice
方法,就像上面提到的其他方法一样,它是“故意通用的”,因此可以与类似数组的对象一起使用,如下所示:
var trueArray = Array.prototype.slice.call(arrayLikeObject);
因此,例如,如果我们想将NodeList
转换为一个真实的数组,我们可以这样做:
var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
请参阅下面的警告,了解主机提供的对象 。 特别要注意的是,这将在IE8及更早版本中失败,这不能让您像this
使用主机提供的对象。
使用
还可以将ES2015的与支持此功能的JavaScript引擎一起使用:
var trueArray = [...iterableObject];
因此,例如,如果我们想将NodeList
转换为一个真实的数组,使用扩展语法,这将变得非常简洁:
var divs = [...document.querySelectorAll("div")];
使用Array.from
|
Array.from
(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
方法都依赖于主机提供的对象,该对象为抽象操作提供了诚实的答案。 在撰写本文时,浏览器在这方面做得很好,但是5.1规范确实允许由主机提供的对象可能不诚实。 在 ,该部分开头附近的大表下方的几段中),其中表示:
除非另有说明,否则宿主对象可以以任何方式实现这些内部方法。 例如,一种可能性是特定宿主对象的
[[Get]]
和[[Put]]
确实会获取并存储属性值,但[[HasProperty]]
始终会生成false 。
(我在ES2015规范中找不到等效的术语,但情况肯定仍然如此。)同样,在撰写本文时,现代浏览器中常见的由主机提供的类似数组的对象[例如NodeList
实例] 可以正确处理[[HasProperty]]
,但进行测试很重要。)
注意 :此答案已过时。 对于更现代的方法,请查看 。 感兴趣的方法可能是:
迭代以阵列的标准方法是香草for
-loop:
var length = arr.length, element = null;for (var i = 0; i < length; i++) { element = arr[i]; // Do something with element}
但是请注意,这种方法仅在数组密集且每个索引都被一个元素占用的情况下才是好的。 如果数组稀疏,则使用此方法会遇到性能问题,因为您将遍历数组中实际上不存在的许多索引。 在这种情况下, for .. in
。in-loop可能是一个更好的主意。 但是 ,您必须使用适当的防护措施来确保仅对数组的所需属性(即数组元素)进行操作,因为for..in
-loop也将在旧版浏览器中枚举,或者如果其他属性被定义为enumerable
。
在 ,数组原型上将有一个forEach方法,但是旧版浏览器不支持该方法。 因此,要能够始终如一地使用它,您必须具有一个支持它的环境(例如,服务器端JavaScript的 ),或使用“ Polyfill”。 但是,用于此功能的Polyfill很简单,并且由于它使代码更易于阅读,因此它是一个很好的polyfill。
如果要循环遍历数组,请使用标准的三部分式for
循环。
for (var i = 0; i < myArray.length; i++) { var arrayItem = myArray[i];}
您可以通过缓存myArray.length
或向后对其进行迭代来获得一些性能优化。
本机 for each
循环都没有。 您可以使用库来获得此功能(我建议 ),也可以使用简单的for
in循环。
for (var instance in objects) { ...}
但是,请注意,可能有理由使用甚至更简单的for
循环(请参阅Stack Overflow问题, )
var instance;for (var i=0; i < objects.length; i++) { var instance = objects[i]; ...}
一些风格的语言使用foreach
枚举。 在JavaScript中,这是通过 :
var index, value;for (index in obj) { value = obj[index];}
有一个陷阱。 for..in
将for..in
对象的每个可枚举成员,以及其原型上的成员。 为了避免读取通过对象原型继承的值,只需检查属性是否属于对象:
for (i in obj) { if (obj.hasOwnProperty(i)) { //do stuff }}
此外, 在Array.prototype
添加了一个方法,该方法可用于使用回溯对数组进行枚举(polyfill在文档中,因此您仍可将其用于旧版浏览器):
arr.forEach(function (val, index, theArray) { //do stuff});
重要的是要注意,当回调返回false
时, Array.prototype.forEach
不会中断。 和提供自己的变化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://judnb.baihongyu.com/