对于JavaScript中的每个数组?

2024-04-19 16:11:46 发布

您现在位置:Python中文网/ 问答频道 /正文

如何使用JavaScript循环遍历数组中的所有条目?

我以为是这样的:

forEach(instance in theArray)

其中theArray是我的数组,但这似乎不正确。


Tags: instancein条目数组javascriptforeachthearray
30条回答

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];

TL;DR

  • 不要使用for-in,除非你使用它时有安全措施,或者至少知道它为什么会咬你。
  • 你最好的下注通常是

    • 循环(仅限ES2015+)
    • Array#forEach^{}| ^{})(或其亲属some等)(仅限ES5+)
    • 一个简单的老式循环
    • 或者有安全措施。

但是还有很多东西要探索,继续阅读。。。


JavaScript具有强大的语义,可以在数组和类似数组的对象之间循环。我将答案分为两部分:用于真正数组的选项,以及用于类似于的数组的选项,例如arguments对象、其他iterable对象(ES2015+)、DOM集合等等。

我很快就会注意到,您可以使用ES2015选项现在,甚至在ES5引擎上,通过ES2015转换为ES5。搜索“ES2015传输/ES6传输”了解更多。。。

好吧,让我们看看我们的选择:

对于实际数组

在目前最广泛支持的版本ECMAScript 5(“ES5”)中有三个选项,在ECMAScript 2015(“ES2015”,“ES6”)中又增加了两个选项:

  1. 使用forEach和相关(ES5+)
  2. 使用简单的for循环
  3. 正确使用for-in
  4. 使用for-of(隐式使用迭代器)(ES2015+)
  5. 显式使用迭代器(ES2015+)

详细信息:

一。使用forEach和相关的

在任何模糊的现代环境(因此,不是IE8)中,您可以使用由ES5添加的Array功能(直接或使用polyfill),您可以使用forEach^{}{a2}):

&13;
&13;
var a = ["a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

forEach接受回调函数,并可选地接受在调用回调时用作this的值(上面未使用)。按顺序为数组中的每个条目调用回调,跳过稀疏数组中不存在的条目。尽管我只使用了上面的一个参数,但回调是用三个参数调用的:每个项的值、该项的索引和对正在迭代的数组的引用(以防您的函数不方便使用)。

除非您支持像IE8这样的过时浏览器(截至2016年9月,NetApps的市场份额刚刚超过4%),否则您可以在没有垫片的通用网页中愉快地使用forEach。如果您确实需要支持过时的浏览器,那么填隙/polyfillingforEach很容易完成(在“es5 shim”中搜索几个选项)。

forEach的好处是,您不必在包含范围内声明索引和值变量,因为它们是作为迭代函数的参数提供的,而且范围很好地限定为该迭代。

如果您担心为每个数组项调用函数的运行时开销,请不要;details

另外,forEach是“循环遍历它们”函数,但是ES5定义了其他几个有用的“遍历数组并执行任务”函数,包括:

  • ^{}(在回调第一次返回false或其他错误消息时停止循环)
  • ^{}(在回调第一次返回true或某个truthy时停止循环)
  • ^{}(创建一个新数组,其中包含filter函数返回true的元素,并省略返回false的元素)
  • ^{}(根据回调返回的值创建新数组)
  • ^{}(通过反复调用回调函数并传入以前的值来构建一个值;有关详细信息,请参见规范;它对于总结数组的内容和许多其他内容非常有用)
  • ^{}(类似于reduce,但按降序而不是升序工作)

2。使用一个简单的for循环

有时旧的方法是最好的:

&13;
var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
    console.log(a[index]);
}

如果在循环过程中数组的长度不会改变,并且它在性能敏感的代码中(不太可能),一个稍微复杂一点的版本获取前面的长度可能会快一点:

&13;
&13;
var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
    console.log(a[index]);
}

和/或倒数:

&13;
&13;
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"

&13;
&13;
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);
    });
}

&13;
&13;
let 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>

如果你有五个div,如果你点击了第一个,你会得到“Index is:0”,如果你点击了最后一个,你会得到“Index is:4”。如果您使用var而不是let,则这不会起作用。

三。正确使用for-in

人们会告诉你使用for-in,但是that's not what ^{} is forfor-in循环遍历对象的可枚举属性,而不是数组的索引。订单不保证,甚至在ES2015(ES6)中也不保证。ES2015+确实定义了一个order-to-object属性(通过^{}^{},以及使用它们的东西,比如^{}),但是它并没有定义for-in将遵循这个顺序。(详情见this other answer。)

数组上for-in的唯一实际用例是:

  • 这是一个sparse arrays有巨大的间隙,或者
  • 您正在使用非元素属性,并希望将它们包含在循环中

仅看第一个示例:如果使用适当的保护措施,可以使用for-in访问这些sparese数组元素:

&13;
&13;
// `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]);
    }
}

注意三项检查:

  1. 对象的拥有该名称的属性(不是从其原型继承的),并且

  2. 密钥是所有十进制数字(例如,普通字符串形式,而不是科学符号),并且

  3. 当强制为数字时,键的值为<;=2^32-2(即4294967294)。这个号码是哪里来的?它是数组索引in the specification定义的一部分。其他数字(非整数、负数、大于2^32-2的数字)不是数组索引。它是2^32-2的原因是,这使得最大索引值小于2^32-1,这是数组的^{可以拥有的最大值。(例如,一个数组的长度适合一个32位无符号整数。)(这是RobG在注释on my blog post中指出我之前的测试不太正确的建议。)

当然,在内联代码中不会这样做。你要写一个实用函数。也许:

&13;
&13;
// 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";
a.b = "bee";

sparseEach(a, function(value, index) {
    console.log("Value at " + index + " is " + value);
});

四。使用for-of(隐式使用迭代器)(ES2015+

ES2015将迭代器添加到JavaScript中。使用迭代器的最简单方法是新的for-of语句。看起来是这样的:

&13;
&13;
const a = ["a", "b", "c"];
for (const val of a) {
    console.log(val);
}

在封面下,它从数组中获取一个迭代器并循环遍历它,从中获取值。这与使用for-in没有问题,因为它使用由对象(数组)定义的迭代器,而数组定义其迭代器遍历其(不是它们的属性)。与ES5中的for-in不同,访问条目的顺序是其索引的数字顺序。

5个。显式使用迭代器(ES2015+)

有时,您可能希望显式地使用迭代器。你也可以这样做,尽管它比for-of要笨重得多。看起来是这样的:

&13;
&13;
const a = ["a", "b", "c"];
const it = a.values();
let entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

迭代器是与规范中的迭代器定义匹配的对象。它的next方法每次调用时都返回一个新的结果对象。result对象有一个属性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对象等。我们如何循环它们的内容?

对于数组

上述数组方法中,至少有一些(可能是大多数,甚至是全部)通常同样适用于类似数组的对象:

  1. 使用forEach和相关(ES5+)

    Array.prototype上的各种函数是“有意泛型的”,通常可以通过^{}^{}在数组类对象上使用。(请参见本答案末尾的主机提供的对象注意事项,但这是一个罕见的问题。)

    假设您想对NodechildNodes属性使用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`
    });
    
  2. 使用简单的for循环

    显然,一个简单的for循环适用于类似数组的对象。

  3. 正确使用for-in

    使用与数组相同的保护机制的for-in也应该使用类似数组的对象;上面1中主机提供的对象的警告可能适用。

  4. 使用for-of(隐式使用迭代器)(ES2015+)

    for-of将使用对象(如果有的话)提供的迭代器;我们必须看看这如何处理各种类似数组的对象,特别是主机提供的对象。例如,来自querySelectorAllNodeList的规范被更新为支持迭代。来自getElementsByTagNameHTMLCollection的规范不是。

  5. 显式使用迭代器(ES2015+)

    看#4,我们得看看迭代器是如何运行的。

创建一个真正的数组

有时,您可能希望将类似数组的对象转换为真正的数组。这样做非常容易:

  1. 使用数组的^{}方法

    我们可以使用数组的slice方法,与上面提到的其他方法一样,该方法是“有意通用的”,因此可以用于类似数组的对象,例如:

    var trueArray = Array.prototype.slice.call(arrayLikeObject);
    

    例如,如果我们想将一个NodeList转换成一个真正的数组,我们可以这样做:

    var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
    

    请参阅主机提供的对象的警告下面。特别要注意的是,这在IE8和更早版本中会失败,因为IE8不允许您像那样使用主机提供的对象this

  2. 使用spread syntax (^{})

    还可以将ES2015的spread syntax与支持此功能的JavaScript引擎一起使用:

    var trueArray = [...iterableObject];
    

    例如,如果我们想将一个NodeList转换成一个真正的数组,使用spread语法这将变得非常简洁:

    var divs = [...document.querySelectorAll("div")];
    
  3. 使用Array.from(spec)| (MDN)

    Array.from(ES2015+,但很容易polyfilled)从类似数组的对象创建数组,可以选择先通过映射函数传递条目。所以:

    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函数与主机提供的数组类对象(由浏览器而不是JavaScript引擎提供的DOM列表和其他内容)一起使用,则需要确保在目标环境中进行测试,以确保主机提供的对象正常工作。大多数人确实表现得很好(现在),但测试很重要。原因是,您可能希望使用的大多数Array.prototype方法都依赖于宿主提供的对象对抽象^{}操作给出了一个诚实的答案。在撰写本文时,浏览器在这方面做得非常好,但是5.1规范确实允许主机提供的对象可能不诚实。它在§8.6.2中,在靠近该部分开头的大表下面的几段中,上面写着:

Host objects may implement these internal methods in any manner unless specified otherwise; for example, one possibility is that [[Get]] and [[Put]] for a particular host object indeed fetch and store property values but [[HasProperty]] always generates false.

(我在ES2015规范中找不到等效的措辞,但它肯定仍然是这样的。)同样,在编写本文时,现代浏览器中的公共主机提供的类似数组的对象[NodeList实例,例如]do正确处理[[HasProperty]],但测试很重要。)

如果您使用的是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].
}

AforEach实现(see in jsFiddle):

function forEach(list,callback) {
  var length = list.length;
  for (var n = 0; n < length; n++) {
    callback.call(list[n]);
  }
}

var myArray = ['hello','world'];

forEach(
  myArray,
  function(){
    alert(this); // do something
  }
);

这是非稀疏列表的迭代器,索引从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标签变成了绿色

使用字符串作为函数时请注意:该函数是在上下文之外创建的,应仅在确定变量范围时使用。否则,最好传递范围更直观的函数。

向后循环

我认为for循环值得一提:

for (var i = array.length; i--; ) {
     // process array[i]
}

优点:

  • 您不需要在每次迭代中声明临时的len变量,也不需要与array.length进行比较,这两种方法都可能是一分钟的优化。
  • 按相反的顺序从DOM中删除兄弟节点通常会更有效。(浏览器需要减少内部数组中元素的移动。)
  • 如果在循环期间、索引i处或索引i之后修改数组(例如,在array[i]处删除或插入一个项),则前向循环将跳过左移到位置i的项,或重新处理右移的第i项。在传统的for循环中,可以updatei指向下一个需要处理的项-1,但是简单地反转迭代方向通常是一个简单的more elegant solution
  • 类似地,当修改或删除嵌套的DOM元素时,反向处理可以避免错误。例如,考虑在处理父节点的子节点之前修改其父节点的innerHTML。当到达子节点时,它将与DOM分离,在编写父节点的innerHTML时,它已被新创建的子节点替换。
  • 与其他一些可用选项相比,它的键入时间短于,读取时间短于。尽管它输给了forEach()和ES6的for ... of

缺点:

  • 它按相反的顺序处理项目。如果您正在根据结果构建新数组,或在屏幕上打印内容,则输出自然会与原始顺序相反。
  • 重复地将兄弟节点作为第一个子节点插入到DOM中以保持它们的顺序是效率较低的。(浏览器必须不断地右移。)要高效有序地创建DOM节点,只需向前循环并正常追加(同时使用“文档片段”)。
  • 对于初级开发人员来说,反向循环是令人费解的。(你可能会认为这是一个优势,这取决于你的前景。)

我应该一直用它吗?

有些开发人员在默认情况下使用反向循环,除非有充分的理由循环转发。

虽然性能的提高通常是微不足道的,但它有点像尖叫:

"Just do this to every item in the list, I don't care about the order!"

然而在实践中,这并不是一个真正可靠的意图指示,因为它与那些需要逆循环的情况是不可区分的。因此,实际上需要另一个构造来准确地表达“不在乎”的意图,这在大多数语言(包括ECMAScript)中都是不可用的,但可以称之为forEachUnordered()

如果顺序无关紧要,并且效率是一个问题(在游戏或动画引擎的最里面的循环中),那么可以使用反向for循环作为转到模式。只需记住,在现有代码中看到for reverse for循环并不一定意味着顺序不相关!

最好使用forEach()

一般来说,对于更高级别的代码来说,清晰性和安全性更为重要,我以前建议使用^{}作为循环的默认模式(尽管现在我更喜欢使用for..of)。比起反向循环,更喜欢forEach的原因是:

  • 读起来更清楚。
  • 它表明i不会在块内移动(这可能总是隐藏在长的forwhile循环中)。
  • 它为闭包提供了一个自由的范围。
  • 它是减少局部变量的泄漏和与外部变量的意外碰撞(和变异)。

然后,当您在代码中看到for reverse for循环时,这意味着它是为了一个好的原因(可能是上面描述的原因之一)而被反转的。而看到传统的前向循环可能意味着可以发生移位。

(如果对意图的讨论对您没有意义,那么您和您的代码可能会从观看Crockford关于Programming Style & Your Brain的演讲中受益。)

它现在更适合用于..的!

关于for..offorEach()是否更可取,存在着争论:

  • 为了获得最大的浏览器支持,for..ofrequires a polyfill为迭代器,使您的应用程序执行稍慢,下载稍大。

  • 为此(并鼓励使用mapfilter),完全禁止some front-end style guides{}!

  • 但上述问题不适用于Node.js应用程序,在Node.js应用程序中,for..of现在得到了很好的支持。

  • 而且awaitdoes not workforEach()内部。在这种情况下,使用for..of就是the clearest pattern

就我个人而言,我倾向于使用任何看起来最容易阅读的东西,除非性能或小型化已经成为一个主要问题。所以这些天我更喜欢用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开始?

    因为i--在每次迭代之前都会运行,所以在第一次迭代时,我们实际上会访问array.length - 1处的项,这样就避免了数组越界undefined项的任何问题。

  • 为什么不在索引0之前停止迭代?

    当条件i--计算为错误值时(当它产生0时),循环将停止迭代。

    诀窍在于,与--i不同,尾随的i--运算符递减i,但在递减之前会产生值。您的控制台可以演示:

    > var i = 5; [i, i--, i];

    [5, 5, 4]

    所以在最后一次迭代中,i以前是1,而i--表达式将其更改为0,但实际上产生1(truthy),因此条件通过。在下一次迭代中i--i更改为-1,但产生0(错误),导致执行立即退出循环的底部。

    在传统的前向循环中,i++++i是可互换的(正如道格拉斯·克罗克福德指出的那样)。然而,在for循环的相反部分,因为减量也是条件表达式,如果要处理索引0处的项,则必须使用i--


琐事

有些人喜欢在反向循环中画一个小箭头,然后以眨眼结束:

for (var i = array.length; i --> 0 ;) {

学分交给WYL,让我看到反向循环的好处和可怕之处。

我还想添加这作为一个反向循环的组成部分,以及上面的一个答案,供希望使用此语法的人使用。

var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
    console.log(item);
}

专业人士:

这样做的好处是:在第一行中已经有了引用,这样以后就不需要用另一行声明了。当通过对象数组循环时,它很方便。

缺点:

当引用为假-假(未定义等)时,这将中断。不过,它可以作为一种优势。不过,这会让它更难阅读。而且也取决于浏览器,它可以“不”优化,比原来的工作速度更快。

我知道这是一篇老文章,已经有很多好的答案了。为了更完整一点,我想我可以用AngularJS再加一个。当然,这只适用于你使用角度,显然,尽管如此,我还是想把它。

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循环。现在,我将在cons部分获得效率,因为forEach循环比for循环慢得多。我提到这是一个专业,因为它很好的一致性和标准化。

考虑下面两个嵌套循环,它们做的是完全相同的事情。假设我们有两个对象数组,每个对象包含一个结果数组,每个结果数组都有一个值属性,它是一个字符串(或其他什么)。假设我们需要迭代每个结果,如果它们相等,那么执行一些操作:

angular.forEach(obj1.results, function(result1) {
    angular.forEach(obj2.results, function(result2) {
        if (result1.Value === result2.Value) {
            //do something
        }
    });
});

//exact same with a for loop
for (var i = 0; i < obj1.results.length; i++) {
    for (var j = 0; j < obj2.results.length; j++) {
        if (obj1.results[i].Value === obj2.results[j].Value) {
            //do something
        }
    }
}

虽然这是一个非常简单的假设性例子,但我已经使用第二种方法编写了三重嵌入式for循环,而且很难读取和写入。

缺点

  • 效率。angular.forEach和本地的forEach都比正常的for循环慢得多。因此,对于大型数据集,最好坚持使用本机for循环。
  • 无中断、继续或返回支持。continue实际上由“accident”支持,要在angular.forEach中继续,只需在类似angular.forEach(array, function(item) { if (someConditionIsTrue) return; });的函数中放入一个return;语句,这将导致它在该迭代的函数外继续。这也是因为本机forEach不支持中断或继续。

我相信还有其他的优点和缺点,请随意添加任何你认为合适的。我觉得,归根结底,如果你需要效率,就只使用原生的循环来满足你的循环需求。但是,如果你的数据集更小,为了可读性和可写性放弃某种效率是可以的,那么一定要给那个坏孩子一个angular.forEach

可能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
//!!!

如果要在数组上循环,请使用标准的三部分循环。

for (var i = 0; i < myArray.length; i++) {
    var arrayItem = myArray[i];
}

通过缓存myArray.length或向后迭代,可以获得一些性能优化。

没有内在的能力来突破forEach。要中断执行,请使用如下Array#some

[1,2,3].some(function(number) {
    return number === 1;
});

这是因为只要按数组顺序执行的任何回调返回true,那么some就会返回true,从而使其余回调的执行短路。 Original Answer 请参见some的数组原型

注意:这个答案毫无希望地过时了。要获得更现代的方法,请查看the methods available on an array。感兴趣的方法可能是:

  • forEach公司
  • 地图
  • 过滤器
  • 拉链
  • 减少
  • 一些

JavaScript中迭代数组的标准方法是普通的for循环:

var length = arr.length,
    element = null;
for (var i = 0; i < length; i++) {
  element = arr[i];
  // Do something with element
}

但是,请注意,这种方法只有在拥有密集数组且每个索引都被元素占用时才有效。如果数组是稀疏的,那么使用这种方法可能会遇到性能问题,因为您将迭代数组中不存在真正的索引。在这种情况下,一个for .. in循环可能是一个更好的主意。但是,必须使用适当的保护措施来确保仅对数组的所需属性(即数组元素)进行操作,因为for..in循环也将在旧版浏览器中枚举,或者如果其他属性定义为enumerable

ECMAScript 5中,数组原型上将有一个forEach方法,但在传统浏览器中不支持它。因此,要一致地使用它,您必须拥有支持它的环境(例如,对于服务器端JavaScript,Node.js),或者使用“Polyfill”。不过,此功能的Polyfill很简单,因为它使代码更易于阅读,所以它是一个很好的Polyfill。

总结:

在遍历数组时,我们通常希望实现以下目标之一:

  1. 我们希望遍历数组并创建新数组:

    Array.prototype.map

  2. 我们希望遍历数组而不创建新数组:

    Array.prototype.forEach

    for..of循环

在JavaScript中,有很多方法可以实现这两个目标。不过,有些比其他更方便。下面您可以找到一些常用的方法(最方便的IMO)来完成JavaScript中的数组迭代。

创建新数组:Map

map()是位于Array.prototype上的一个函数,它可以转换数组中的每个元素,然后返回一个new数组。map()接受回调函数作为参数,并按以下方式工作:

&13;
&13;
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与其他循环机制(如forEachfor..of循环)之间的显著区别是,map作为新数组返回,并保留旧数组的完整性(除非使用类似于splice的思想显式操作它)。

还要注意,map函数的回调函数提供当前迭代的索引号作为第二个参数。此外,第三个参数是否提供调用map的数组。有时这些属性非常有用。

使用forEach循环

forEach是位于Array.prototype上的函数,它以回调函数作为参数。然后对数组中的每个元素执行这个回调函数。与map()函数相反,forEach函数不返回任何内容(undefined)。例如:

&13;
&13;
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循环遍历数组(或任何其他iterable对象)的每个元素。其工作方式如下:

&13;
&13;
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循环将只遍历数组元素。例如:

&13;
&13;
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);
}

与您的想法最接近的一种方法是使用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]

现在一个简单的解决方案是使用underscore.js library。它提供了许多有用的工具,比如each,如果可用的话,它会自动将作业委托给本机forEach

它的工作原理是:

var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});

另见

  • Documentation for native ^{}
  • for_each...in(MDN)中,解释了for each (variable in object)作为ECMA-357(EAX)标准的一部分被弃用。
  • for...of(MDN)描述了下一种使用for (variable of object)作为Harmony(ECMAScript 6)建议的一部分的迭代方法。

有几种方法可以在JavaScript中循环遍历数组,如下所示:

对于-它是最常见的。循环的完整代码块

&13;
&13;
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-条件结束时循环。它似乎是最快的循环

&13;
&13;
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时,还循环遍历一个代码块,将至少运行一次

&13;
&13;
var text = ""
var i = 0;

do {
    text += i + ") something <br>";
    i++;
}
while (i < 10);

document.getElementById("example").innerHTML = text;
<p id="example"></p>

函数循环-forEachmapfilter,还有reduce(它们循环遍历函数,但如果需要对数组执行某些操作,则使用它们等)

&13;
&13;
// 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>

有关数组上函数式编程的更多信息和示例,请参阅博客文章Functional programming in JavaScript: map, filter and reduce

如果您想使用forEach(),它看起来像-

theArray.forEach ( element => {
    console.log(element);
});

如果您想使用for(),它看起来像-

for(let idx = 0; idx < theArray.length; idx++){
    let element = theArray[idx];
    console.log(element);
}

您可以使用: 一。ForEach公司

theArray.forEach(function (array, index) {
    console.log(index);
    console.log(array);
});

2.对于

for(var i=0;i<theArray.length;i++){
console.log(i)
}

3.地图

theArray.map(x => console.log(x));

4.地图

theArray.filter(x => console.log(x));

还有很多其他的需要迭代

在本机JavaScript中没有任何for each循环。您可以使用库来获取此功能(我建议使用Underscore.js),也可以使用简单的forin循环。

for (var instance in objects) {
   ...
}

但是,请注意,可能有理由使用更简单的for循环(请参阅堆栈溢出问题Why is using “for…in” with array iteration such a bad idea?

var instance;
for (var i=0; i < objects.length; i++) {
    var instance = objects[i];
    ...
}

如果要使用arrow函数循环遍历对象数组:

&13;
&13;
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');
})

你可以这样给forEach打电话:

let Array = [1,3,2];

theArray.forEach((element) => {
  // Use the element of the array
  console.log(element)
}

element将每个索引的值从0到数组的长度。

输出:

1
3
2

说明:

forEach在prototype类中。您也可以将其称为array.prototype.forEach(…)

原型:

Prototypes in JavaScript

您还可以对数组进行如下迭代:

for (let i=0; i<theArray.length; i++) {
  console.log(i); // i will have the value of each index
}

如果你有一个巨大的数组,你应该使用^{}来获得一些效率。迭代器是某些JavaScript集合的属性(比如^{}^{}^{}^{})。甚至,^{}在引擎盖下使用iterator

迭代器通过让您像使用流一样一次一个地使用列表中的项来提高效率。迭代器的特殊之处在于它如何遍历集合。其他循环需要预先加载整个集合,以便对其进行迭代,而迭代器只需要知道集合中的当前位置。

通过调用迭代器的next方法访问当前项。下一个方法将返回当前项的value,并返回一个boolean以指示何时到达集合的结尾。下面是从数组创建迭代器的示例。

使用^{}方法将正则数组转换为迭代器,如下所示:

&13;
&13;
    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());

还可以使用^{}将常规数组转换为迭代器,如下所示:

&13;
&13;
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转换为^{},如下所示:

&13;
&13;
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 protocolhere的更多信息。

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

如果你不介意清空阵列:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

x将包含y的最后一个值,它将从数组中删除。您还可以使用shift(),它将给出并从y中删除第一个项。

与ECMAScript 6destructuringspread operator一起使用循环

事实证明,对ECMAScript 6的新手来说,spread操作符的解构和使用非常有用,因为它更具可读性/美感,尽管一些JavaScript老手可能认为它很混乱。年轻人或其他人可能会觉得这很有用。

The following examples will use the for...of statement and the .forEach method.

Examples 6, 7, and 8 can be used with any functional loops like .map, .filter, .reduce, .sort, .every, .some. For more information about these methods, check out the Array Object.

示例1:Normalfor...of循环-这里没有技巧。

&13;
&13;
let arrSimple = ['a', 'b', 'c'];

for (let letter of arrSimple) {
  console.log(letter);
}

示例2:将单词拆分为字符

&13;
&13;
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:使用keyvalue循环

&13;
&13;
// 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:内联获取对象属性

&13;
&13;
let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];

for (let { name, age: aliasForAge } of arrWithObjects) {
  console.log(name, aliasForAge);
}

示例5:获取所需的深层对象属性

&13;
&13;
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:.forEach一起使用

&13;
&13;
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一起使用

&13;
&13;
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一起使用

&13;
&13;
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);
});

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);
});

如果您是jQueryfan并且已经有一个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);
})

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

every-如果数组中的所有元素都通过回调函数中的测试,则返回true或false。

// Check if everybody has 18 years old of more.
var ages = [30, 43, 18, 5];
ages.every(function(elem) {
  return elem >= 18;
});

// Output: false

filter-非常类似于除filter返回一个数组外,该数组的元素返回给给定函数的true。

// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
  return (elem % 2 == 0)
});

// Output: [2,4,6]

有些C风格的语言使用foreach循环遍历枚举。在JavaScript中,这是通过^{} loop structure完成的:

var index,
    value;
for (index in obj) {
    value = obj[index];
}

有一个陷阱。for..in将遍历对象的每个可枚举成员及其原型上的成员。为了避免读取通过对象原型继承的值,只需检查属性是否属于该对象:

for (i in obj) {
    if (obj.hasOwnProperty(i)) {
        //do stuff
    }
}

此外,ECMAScript 5还向Array.prototype添加了一个^{}方法,该方法可用于使用calback在数组上枚举(polyfill位于文档中,因此您仍然可以将其用于较旧的浏览器):

arr.forEach(function (val, index, theArray) {
    //do stuff
});

需要注意的是,当回调返回false时,Array.prototype.forEach不会中断。jQueryUnderscore.jseach上提供自己的变体,以提供可以短路的循环。

从ECMAScript 6起:

&13;
&13;
list = [0, 1, 2, 3]
for (let obj of list) {
    console.log(obj)
}

其中of避免了与in相关联的奇怪之处,使其工作方式类似于任何其他语言的for循环,并且let在循环中绑定i,而不是在函数中绑定。

只有一个命令时(例如,在上面的示例中),可以省略大括号({})。

相关问题 更多 >