稀疏数组并减少 JavaScript

Sparse arrays and reduce in JavaScript

人们会认为在 JavaScript 中: var array = [1,2,undefined,4]; 等同于:

var array = [1,2];
array.length = 3;
array.push(4);

但事实并非如此。此代码显示它:

var array1 = [1,2];
array1.length = 3;
array1.push(4);

var array2 = [1,2,undefined,4];

traverseArray(array1);
traverseArray(array2);

function traverseArray(array) {
  console.log("trying: " + array);
  console.log("reduce:");
  array.reduce(function(prev, current, index, array) {
    if(current === undefined) {
      console.log("Found undefined");
    }
    console.log(index+": " +current);
  }, undefined);

  console.log("for loop:")
  for(var i=0;i < array.length;i++) {
    var current = array[i];
    console.log(i+": " +current);
  }
  console.log();
}

输出:

trying: 1,2,,4
reduce:
0: 1
1: 2
3: 4
for loop:
0: 1
1: 2
2: undefined
3: 4

trying: 1,2,,4
reduce:
0: 1
1: 2
Found undefined
2: undefined
3: 4
for loop:
0: 1
1: 2
2: undefined
3: 4

为什么 array1 中的 undefined 与 array2 中的 undefined 不同,为什么 for 循环的行为相同但 reduce 不同?

apsillers 在评论中指出了这一点。 . .不同之处在于,在 array2 中,您实际上已将 undefined 值分配给数组中的第三个元素(即在索引 2 处),而在 array1 中,您有两个元素最初,改变长度属性,然后在第四个位置添加第三个元素。

以下是来自 MDN 的相关部分,解释了为什么区别很重要:

  • 来自 Array.length 上的 page

When you extend an array by changing its length property, the number of actual elements does not increase; for example, if you set length to 3 when it is currently 2, the array still contains only 2 elements. Thus, the length property says nothing about the number of defined values in the array.

  • 来自 Array.push() 上的 page

The push method relies on a length property to determine where to start inserting the given values.

这里的关键是,length 属性 确实是一个非常简单的 属性,它与数组的内容没有任何内在联系。只是因为Array的各种方法恰好也维护属性,因为它们"do their work",所以我的行为就好像是

因此,在 array2 中,代码实际上报告了您分配给 array2[2]undefined 值,而在 array1 中,该代码将 array1[2] 处缺少值解释为 undefined.

array1 具有三个以数字命名的属性:013.

array2 有四个以数字命名的属性:0123。名为 2 的 属性 的值恰好是 undefined.

当你向一个对象询问它没有的 属性 的值时,结果是 undefined.

for 循环中,您向每个数组询问其名为 0123 的属性的值。对于 array1,名为 2 的 属性 不存在,因此 属性 访问会产生 undefined。对于array2,属性确实存在,但它的值实际上undefined,所以你得到同样的结果。

另一方面,reduce 仅对实际存在的属性进行操作。 From the ECMAScript specification,这就是 reduce 循环数组的方式,使用计数器 k:

  1. Repeat, while k < len
    • Let Pk be ToString(k).
    • Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
    • If kPresent is true, then... [use the value at index k for the reduce call]

因此,我们可以看到索引仅在通过 [[HasProperty]] 检查时才使用。 array1 没有名为 2 的 属性,因此跳过该索引。