为什么需要复制数组以在其上使用方法?

时间:2018-11-27 02:35:48

标签: javascript arrays

我可以使用Array()来创建一个带有固定数量未定义条目的数组。例如

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

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

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

如果我复制数组,则映射确实起作用:

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

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

4 个答案:

答案 0 :(得分:50)

使用Array(arrayLength)创建数组时,您将have

  

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

该数组实际上不包含任何值,甚至不包含undefined值-它只是具有length属性。

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

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

答案 1 :(得分:9)

原因是未分配数组元素。 See here说明的第一段。 ...回调仅对已分配了值(包括未定义)的数组索引进行调用。

考虑:

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

答案 2 :(得分:2)

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

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

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

        
                
  1.             让              Pk 是ToString( k )。

                
  2.             
  3.             让              kPresent 是调用[[HasProperty]]的结果              O 的内部方法,参数为 Pk

                
  4.             
  5.             如果              kPresent true ,然后是

                
                      
    1.                 让                  kValue 是调用内部[[Get]]的结果                 参数为 Pk O 方法。

                      
    2.                 
    3.                 让                  mappedValue 是调用内部[[Call]]的结果                  callbackfn 的方法,其中 T 作为 this                 包含 kValue k 和                  O

                      
    4.                 
    5.                 呼叫                  A 的[[DefineOwnProperty]]内部方法,                 参数 Pk ,属性描述符{[[Value]]: mappedValue ,                 [[Writable]]: true ,[[Enumerable]]: true ,                 [[可配置]]: true }和 false

                  
                
  6.             
  7.             增加              k 乘1。

            
         9.返回 A

答案 3 :(得分:0)

正如已经指出的那样,Array(2)将仅创建两个无法映射的空插槽的数组。

someevery而言,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);