【问题标题】:Using a for each loop on an empty array in JavaScript [duplicate]在JavaScript中的空数组上使用for each循环[重复]
【发布时间】:2014-12-11 21:52:03
【问题描述】:

我发现我不能在 javascript 的空数组上使用 for each 循环。谁能给我解释一下这是为什么?

我已经像这样在 javascript 中初始化了一个数组:

var arr = new Array(10);

当我在数组上使用 for each 循环时,什么也没有发生:

arr.forEach(function(i) {
    i = 0;
});

结果仍然是一个未定义值的数组:

arr = [ , , , , , , , , , ];

我认为可能由于数组中的每个项目都未定义,因此它甚至不会执行 forEach。我认为它仍然会遍历未定义的项目。谁能解释为什么会这样? 这个问题不是问如何最有效地用零填充数组,而是询问每个循环与空数组的交互的详细信息

【问题讨论】:

  • 某处有一个流行的 dup...Array.apply(0, Array(10)).map(function(){return 0})
  • 这是一个奇怪的结果,我还以为你有一个长度为 10 的空数组?
  • @adeneo:这是一个包含 10 个 undefineds 的数组。
  • @RocketHazmat - 当然会,我很困惑!
  • @RocketHazmat 警告数组会给出一个逗号列表(至少在 FF 中,尽管我认为在其他浏览器中也是如此)。有趣的是console.log 给了Array [ <10 empty slots> ]

标签: javascript


【解决方案1】:

如果将数组初始化修改为:

var arr = Array.apply(null, Array(10))

然后你可以做一个像这样的foreach:

arr.forEach(function(el, index) {
    arr[index] = 0;
});

结果是:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

【讨论】:

  • 这个会影响性能吗
  • 算法性能不受影响。但是,在谈论性能时,最明智的选择是对自己进行微基准测试。
【解决方案2】:

你说对了一半!

我认为可能由于数组中的每个项目都未定义,因此它甚至不执行 forEach。

Array.prototype.forEach 不访问已删除或省略的索引;这是一个称为省略的过程。因此,它会执行,但会跳过每个元素。

来自MDN

【讨论】:

  • 这并不完全正确。如果您在下面的答案中使用 var arr = Array.apply(null, Array(10)) ,它将按照 OP 的意图正确循环遍历未定义的值。
  • 要详细说明@TheZenCoder 的评论,也许你应该修改你的文本,说 "Array.prototype.forEach 不访问已被删除或删除的索引",如所述在MDN 参考中。它确实访问了undefined的元素。
【解决方案3】:

.forEach 为数组中的每个元素运行您的函数。设置i 的值没有任何作用,它不是参考。

只需使用普通的for 循环:

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

或者你可以不做new Array(10),而是做:

var arr = [];
for(var i = 0; i < 10; i++){
    arr[i] = 0;
}

【讨论】:

  • 我喜欢这个答案而不是 Qambar 的。亲吻。
  • 确实设置i 的值不会做任何事情,因为它不是参考。然而,在这种情况下,由于亚当的回答中描述的省略,它甚至没有走那么远。这是example
  • @SimonMᶜKenzie:谢谢。我没有意识到这个函数甚至不会运行,因为它的值是undefined
  • @RocketHazmat 这不是真的,你可以为每个数组填充未定义的值
【解决方案4】:

基于Faris Zacina answer,一种单行方法。

var zeros = Array.apply(null, Array(10)).map(function(){return 0})

【讨论】:

  • 请问这是干什么用的?
  • 这将创建一个内部有十个零的数组。首先,Array(10) 创建一个包含十个“空”位置的数组,因为空位置不可迭代,我们使用 Array.apply 来绑定 this 对象,我们将其设置为 null,并提供参数作为大批。所有这些都创建了一个 undefineds 数组,它现在是可迭代的,我们可以用 map 填充它
  • 哦..试图赢得一些积分不是你..
【解决方案5】:

控制“省略”的内容

如前所述,存在的键决定了 forEach 是否命中,而长度决定了要检查的键的范围。

对此的另一个有趣的观点是,您可以使用带有陷阱的代理来控制 forEach 和其他迭代方法可见的内容。

我们在这里过滤奇数指标。

let arr = ['zero', 'one', 'two', 'four']
arr.length = 6

let arrProxy = new Proxy(arr, { has(arr, k){ return k%2===0 } })

arrProxy.forEach( val => { console.log(val) })
//zero
//two
//undefined

这样的策略还可以让你获得其他行为,

如果你有 k%2===0 而不是

  • true 你会打到所有的空白空间。
  • k != undefined 仅允许您迭代定义的值。
  • k in arr 带您回到起点。

当然你应该在大多数情况下使用过滤器,这只是一个演示。

【讨论】:

    【解决方案6】:

    如果我错了,希望有人能纠正我

    在V8源代码array.js:1042里面,我发现了这个:

    for (var i = 0; i < length; i++) {
          if (i in array) {
            var element = array[i];
            f(element, i, array);
          }
          //...
    

    重要的是使用'in'运算符进行条件检查,以确定是否执行传递给forEach的函数参数。 in 运算符检查对象内部是否存在属性。

    现在 JS 数组只是一个带有编号属性的奇特对象。即

    var array = [undefined,undefined,undefined];
    Object.keys(array); // ["0", "1", "2"]
    

    对上述数组的另一种思考方式是将其视为像这样的对象

    {
      "0": undefined,
      "1": undefined,
      "2": undefined
    }
    

    但是,如果您使用其他形式来构造数组,您会得到:

    var array2 = new Array(3);
    Object.keys(array2); // []
    

    这就是为什么你会得到以下结果:

    var array = [undefined,undefined,undefined];
    var array2 = new Array(3);
    
    array.forEach(function(){console.log('foo')}); //will print foo three times
    array2.forEach(function(){console.log('bar')}); //will print nothing
    

    【讨论】:

      猜你喜欢
      • 2011-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-02
      • 2015-11-12
      • 1970-01-01
      • 2011-08-31
      相关资源
      最近更新 更多