为何Array(3)无索引0、1、2,而Array.call(null, Array(3))却有?
看起来你大概率是把Array.apply(null, Array(3))写成了Array.call(null, Array(3))——毕竟后者实际会生成一个[Array(3)](length为1,索引0是原稀疏数组)的数组,而你描述的结果(包含0、1、2索引,值为undefined)正好是Array.apply(null, Array(3))的执行效果。我会基于这个符合你描述的场景来详细解释,也会顺带说明Array.call的情况。
为什么Array(3)是稀疏数组
当你用单个有效整数数值调用Array构造函数时,JS引擎会创建一个稀疏数组:
- 它只会设置数组的
length属性为该数值,不会为索引0、1、2添加任何属性——这些位置是所谓的"空槽",和undefined完全不同(你可以用Object.keys(Array(3))验证,结果是空数组,说明这些索引根本不存在)。
为什么Array.apply(null, Array(3))会生成带索引的数组
这要结合apply的特性和Array构造函数的参数处理逻辑来看:
apply的参数展开特性:Function.prototype.apply会把第二个参数(数组/类数组对象)展开为被调用函数的参数列表。对于Array(3)这个稀疏数组,它的length是3,apply会尝试获取索引0、1、2的值,因为这些索引不存在,JS会将它们视为undefined——所以这一步相当于调用Array(undefined, undefined, undefined)。Array构造函数的多参数处理逻辑:当Array构造函数收到多个参数(或单个非数值参数)时,它会创建一个新数组,将每个参数依次赋值给对应的索引(第一个参数→索引0,第二个→索引1,以此类推),同时把length设为参数的个数。- 这里传入了3个
undefined参数,所以数组会显式创建索引0、1、2,每个值为undefined,length为3,这就是你看到的结果。
- 这里传入了3个
如果确实是Array.call(null, Array(3)),它的逻辑是:call会把第二个参数作为单个参数传给Array构造函数,相当于调用Array(Array(3))。因为传入的是单个非数值参数(一个数组对象),所以构造函数会生成一个length为1的数组,索引0的值是原稀疏数组,和你描述的结果不符,所以肯定是笔误啦。
这种方式的具体作用
在Array.from出现之前,Array.apply(null, Array(n))是一种快速把稀疏数组转换成密集数组的常用技巧:
- 它利用
apply的参数展开特性,把稀疏数组的空槽"填充"为undefined,让所有索引都变为实际存在的属性。 - 比如
Array.apply(null, Array(5))会得到[undefined, undefined, undefined, undefined, undefined],索引0-4全部存在。
不过现在更推荐使用Array.from(Array(3))或Array(3).fill(undefined),它们的可读性更好,而且Array.from还支持映射函数(比如Array.from(Array(3), (_, i) => i)可以直接生成[0,1,2]的索引数组)。
内容的提问来源于stack exchange,提问作者Lumaskcete

