每个在JavaScript中的数组?

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

我认为这是这样的:

forEach(instance in theArray)

theArray是我的阵列,但是这似乎是不正确的。


TL; DR

  • 除非您使用保护措施或至少知道它为什么会咬你for-in否则不要使用原装。
  • 你最好的投注通常是

  • 一个for-of循环(仅限ES2015 +),
  • Array#forEachspec | 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 +)
  • 明确使用迭代器(ES2015 +)
  • 细节:

    1.使用forEach和相关的

    如果您使用的环境支持ES5的Array功能(直接或使用垫片),则可以使用新的forEachspec | 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 (第一次停止循环迭代器返回truetrue东西)
  • 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支持entrieskeys但不支持values 。)

    对于类似于数组的对象

    除了真正的数组之外,还有类似数组的对象,它们具有length属性和带有数字名称的属性: NodeList实例, arguments对象等。我们如何遍历它们的内容?

    使用上面的任何选项作为数组

    至少有一些,也可能是大部分甚至全部的数组方法经常适用于类似数组的对象:

  • 使用forEach和相关(ES5 +)

    Array.prototype上的各种函数是“有意通用的”,通常可以通过Function#callFunction#apply类似数组的对象上。 (请参阅此答案结尾处的主机提供的对象警告,但这是一个罕见的问题。)

    假设你想在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`
    });
    
  • 使用一个简单的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]] ,但测试很重要。


    编辑 :这个答案是绝望的过时。 对于更现代的方法,请查看数组上可用的方法。 感兴趣的方法可能是:

  • 的forEach
  • 地图
  • 过滤
  • 压缩
  • 减少
  • 一切
  • 一些

  • 在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/3167.html

    上一篇: each over an array in JavaScript?

    下一篇: Why don't self