为什么map、every和其他数组函数会跳过空值?

19

背景

我正在编写一些代码来检查2个数组是否相同,但是由于某种原因,当期望为false时,结果却为true。经过仔细检查,我发现了那些数组值未定义的地方被跳过了。

示例

const arr1 = [, , 3, 4]
const arr2 = [1, 2, 3, 4]
const result = arr1.every((item, index) => item === arr2[index])
console.log(result) // true (HOW????)

我尝试过的

所以我花了一些时间尝试正确地获取这里的值,但我唯一想到的是一个正常的for循环,它基于数组长度进行迭代而不是实际项目。

问题

为什么会出现这种情况,有没有办法识别我的数组中的这些空/未定义值?


“回调函数仅在数组的已分配值的索引处被调用;它不会在已删除或从未分配值的索引处被调用。” 就在MDN文档中。 - epascarello
4个回答

14

这是因为forEach仅访问实际存在的元素,这是事实的延伸。关于这一点似乎没有更深入的解释,除了在缺少元素时调用回调函数没有太多意义。

您可以通过以下方式实现这些元素(如果这是您所需的):

  1. 展开语法,或者
  2. Array.from,或者
  3. Array.prototype.values,或者
  4. Array.prototype.entries

...或者可能还有其他方法。

const a = [, , 3];
console.log(a.hasOwnProperty(0)); // false
const b = [...a];
console.log(b.hasOwnProperty(0)); // true
const c = Array.from(a);
console.log(b.hasOwnProperty(0)); // true

使用Array.from将其应用于您的函数:

const arr1 = [, , 3, 4]
const arr2 = [1, 2, 3, 4]
const result = Array.from(arr1).every((item, index) => item === arr2[index])
console.log(result) // false

当然,这涉及创建一个新的数组并循环遍历旧的数组,复制元素。你可能最好使用自己的for循环。

Array.prototype.entries应用于您的函数:

const arr1 = [, , 3, 4]
const arr2 = [1, 2, 3, 4]
let result = true;
for (const [index, value] of arr1.entries()) {
    if (value !== arr2[index]) {
        result = false;
        break;
    }
}
console.log(result) // false


5
因为语言设计是这样规定的。 ‍♂️
请参见规范,其中写道:
  • 当 k < len 时,重复以下操作:
    • 令 Pk 为 ToString(k)。
    • 令 kPresent 为 HasProperty(O, Pk)。
    • 如果有异常发生,则立即返回。
    • 如果 kPresent 为 true,则
… 然后执行该操作。
由于从未为属性0和1分配值,因此HasProperty测试会返回false,因此它们会被If规则跳过。

3
根据.every()的文档说明:

回调函数仅会被传入数组中已经赋值的索引,而不会被那些已经被删除或者从未被赋值的索引调用。

因此,你正在使用array1的真实值来调用.every():

const arr1 = [, , 3, 4]

arr1.every((x, idx) => {
 console.log(`element: ${x}`, `index: ${idx}`);
 return true;
})


1
内置的迭代函数(如其他人所描述并在规范中定义的那样)将在 HasProperty 为 false 时跳过值。
您可以为 all 创建自己的 shim,以检查每个值。这将扩展原型。或者,如果此代码将用于更广泛的范围,则将其转换为函数将是更好的设计,并需要稍微不同的调用。

const arr1 = [, , 3, 4];
const arr2 = [1, 2, 3, 4];

Array.prototype.all = function(callback){
 for(let i = 0; i < this.length; i++){
     if(callback(this[i],i,this)) return false;
 }
 return true;
};

const result2 = arr1.all((item, index) => item === arr2[index]);
console.log(result2); // false


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接