JavaScript如何用For-each遍历array

0 投票
最新提问 1月 8, 2017 用户: 与子偕老 (120 分)

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

我想用类似这样的写法,但是好像不对:

forEach(instance in objects)

    1个回答

    0 投票
    最新回答 1月 8, 2017 用户: Mercedes (470 分)

    JavaScript语义强大,用于循环遍历数组和数组类对象。我分成两部分回答:一种是真正的数组,另一种是数组类对象,如arguments对象、迭代对象(ES2015+)、DOM collections等等。

    好吧,让我们来看看我们的选项:

    一、真正的数组

    在ECMAScript 5(“ES5”)中有三个选择,该版本目前支持的最为广泛,并且很快就会有两个新用法将在ECMAScript2015(“ES2015”、“ES6”)提供:

    1. 利用forEach(ES5+)
    2. 用一个简单的for循环
    3. 使用for-in
    4. 使用for-of(使用隐式迭代器)(ES2015+)
    5. 显式使用迭代器(ES2015+)

    细节:

    1.使用forEach

    如果你正在使用的环境支持Array ES5特性,可以使用新的forEach(spec|MDN):

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

    forEach接受一个迭代函数和任意值,以作为使用this调用该迭代函数(上面未使用)时。对于数组中的每个条目,按顺序调用迭代器函数,并且跳过稀疏数组中不存在的条目。

    forEach具有的好处是,您不必在包含作用域中声明索引和值变量,因为它们作为迭代函数的参数提供,因此只是简单地迭代。

    如果你担心为每个数组条目调用函数的运行时成本,不要使用此方法。

    此外,ES5定义了几个其他有用的功能,包括:

    • every(过滤函数第一次返回false时循环停止)
    • some(过滤函数第一次返回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在每个循环中被重新迭代:

    let divs = Array.from(document.querySelector("div"));
    for (let index = 0; index < divs.length; ++index) {
        div[index].addEventListener(e => {
            alert("Index is: " + index);
        });
    }
    

    假设这里有五个div,如果你点击第一个得到“Index is:4”,如果你点击最后一个,你会得到“Index is:0”。

    3.使用for-in

    for-in通过循环的对象的枚举的属性,而不是一个array的索引,遍历的顺序不能保证

    特别是对于稀疏数组,需要采取适当的措施。

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

    注意两个检查:

    1. .hasOwnProperty()属性

    2. 该键是以正常字符串形式的base-10数字字符串,其值为<= 2^32 - 2(这是4,294,967,294)。

    这比大多数数组循环开销大一点,但如果你有一个稀疏的数组,这种方式更有效,因为它只循环实际存在的条目。例如,对于上面的数组,我们一共循环了三次(0,10和10000),而不是10001次。

    如果你不想每次都写这么复杂,可以把它放在你自己的库中:

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

    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
    

    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()
    • keys()
    • entries()

    二、数组类对象

    除了真正的数组,也有类似数组的对象:具有length属性和数字名称属性:如NodeList、arguments等

    对数组使用上述任何选项

    上述数组方法通常可以应用于数组类对象:

    1. 利用forEach

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

      很显然,简单的for循环可以应用到数组类对象。

    3. 使用for-in

      for-in可以在数组类对象上使用。

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

      for-of将使用由对象提供的迭代器(如果有);我们必须检查该对象提供的迭代器函数是否能够正常工作。

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

      参见#4,我们将看看迭代器如何工作。

    创建一个真数组

    其他时候,你可能想将一个类似数组的对象转换为一个真正的数组。

    1. 使用slice数组的方法

      我们可以使用slice数组的方法:

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

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

      var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
      
    2. 使用传播符号(...)

      它也可以使用ES2015的传播符号(MDN目前称之为操作),以支持此功能的JavaScript引擎:

      var trueArray = [...iterableObject];
      

      因此,举例来说,如果我们想将转换NodeList成一个真正的数组,传播语法这变得相当简洁:

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

      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;
      })
      
    欢迎来到编程助手,编程方面有什么不懂的问题可以尽管在这里提问,你将会收到热心爱好者的回答。
    ...