为什么我需要复制一个数组才能使用它的方法?

IT技术 javascript arrays
2021-01-13 09:53:54

我可以使用Array()具有固定数量的未定义条目的数组。例如

Array(2); // [empty × 2] 

但是如果我去使用 map 方法,比如说,在我的新数组上,条目仍然是未定义的:

Array(2).map( () => "foo");  // [empty × 2] 

如果我复制数组,则 map 确实有效:

[...Array(2)].map( () => "foo");  // ["foo", "foo"]

为什么我需要一个副本才能使用数组?

4个回答

当您使用Array(arrayLength)创建数组时,您将拥有

一个新的 JavaScript 数组,其 length 属性设置为该数字(注意:这意味着 arrayLength 空插槽的数组,而不是具有实际未定义值的插槽)。

该数组实际上不包含任何值,甚至不包含undefined值——它只有一个length属性。

当您将具有属性的可迭代对象扩展length到数组中时,扩展语法访问每个索引并在新数组中设置该索引处的值。例如:

const arr1 = [];
arr1.length = 4;
// arr1 does not actually have any index properties:
console.log('1' in arr1);

const arr2 = [...arr1];
console.log(arr2);
console.log('2' in arr2);

并且.map仅映射属性实际存在于您正在映射的数组中的属性/值。

使用数组构造函数令人困惑。我建议使用Array.from,当从头开始创建数组时 - 您可以向它传递一个具有length属性的对象,以及一个映射函数:

const arr = Array.from(
  { length: 2 },
  () => 'foo'
);
console.log(arr);

2021-03-29 09:53:54
Array()在一个课程中看到它让我想起了 Python 中的一些有用的东西,但遗憾的是它可能不是那么有用。干杯。
2021-04-09 09:53:54
曾经是一个不错的选择,之前Array.from可用,但现在我认为没有任何理由使用它,它有太多混淆的可能性。
2021-04-10 09:53:54

原因是数组元素未赋值。请参阅此处描述的第一段。...回调仅针对已分配值(包括未定义)的数组索引调用。

考虑:

var array1 = Array(2);
array1[0] = undefined;

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(array1);
console.log(map1);

输出:

Array [undefined, undefined]
Array [NaN, undefined]

当数组被打印时,它的每个元素都会被询问。第一个已分配undefined,另一个默认为undefined

映射操作调用第一个元素的映射操作,因为它已被定义(通过赋值)。它不会为第二个参数调用映射操作,而只是传递undefined.

正如@CertainPerformance 所指出的,您的数组除了它的 之外没有任何属性length,您可以使用以下行验证:new Array(1).hasOwnProperty(0),它返回false.

查看15.4.4.19 Array.prototype.map可以看到,在7.3 处,检查数组中是否存在

1..6. [...]
7. 重复,而k < len

  1. Pk为 ToString( k )。

  2. kPresent使用参数Pk调用O的 [[HasProperty]] 内部方法的结果

  3. 如果 kPresent,则

    1. kValue使用参数Pk调用O的 [[Get]] 内部方法的结果

    2. mappingValue是调用callbackfn的 [[Call]] 内部方法的结果,其中T作为this 值和包含kValuekO 的参数列表

    3. 调用[[DefineOwnProperty]的内部方法带参数PK,属性描述符{[[价值]]:mappedValue,[可写]]:,[可枚举]]:,[[配置]]:} ,和假的

  4. k增加 1。

9. 返回A

正如已经指出的那样,Array(2)只会创建一个由两个无法映射的空槽组成的数组。

someandevery而言,Array(2)确实是一个空数组:

Array(2).some(() => 1 > 0); //=> false
Array(2).every(() => 1 < 0); //=> true

然而,这个空槽数组可以以某种方式“迭代”:

[].join(','); //=> ''
Array(2).join(','); //=> ','

JSON.stringify([]) //=> '[]'
JSON.stringify(Array(2)) //=> '[null,null]'

因此,我们可以利用这些知识想出有趣且有点讽刺的方法来在此类“空”数组上使用 ES5 数组函数。

你自己想出了那个:

[...Array(2)].map(foo);

来自@charlietfl 的建议的变体:

Array(2).fill().map(foo);

我个人推荐这个:

Array.from(Array(2)).map(foo);

有点老派:

Array.apply(null, Array(2)).map(foo);

这个是相当冗长的:

Array(2).join(',').split(',').map(foo);