JavaScript中为何展开Array(3)得全undefined数组,而Array(3).map返回带空槽数组?
核心概念前置:空槽和存了undefined的位置根本不是一回事
首先要把最容易被混淆的底层差异说透:Array(3)生成的数组里的“空槽”,本质是数组对象上压根没有对应索引的属性键,不是在对应位置存了一个undefined值。
可以用一个简单的验证快速区分两种数组的结构差异:
- 对
Array(3)调用Object.keys(),返回的是一个空数组——因为这个数组根本不存在'0'/'1'/'2'这三个可枚举属性 - 对手动声明的
[undefined, undefined, undefined]调用Object.keys(),会返回['0','1','2']——三个索引键真实存在,键值就是undefined
这两种数组从底层结构上就完全不等价,也是你观察到的两类现象的核心原因。
为什么
map遇到空槽就“跳过” ES规范对map、forEach、filter这类数组迭代方法做了统一的遍历规则:
- 遍历过程中,只会处理数组上真实存在对应属性键的位置
- 碰到没有对应键的空槽时,直接跳过,连你传入的回调函数都不会执行
- 最终返回的新数组里,被跳过的空槽位置也会保留空槽,不会自动填充任何值
对应你的例子:Array(3).map(e => undefined) 全程压根没有执行过你写的回调函数——因为三个位置都没有真实的索引键,所有位置都被map跳过了,最后返回的自然还是带三个空槽的稀疏数组,根本不是预期的全undefined的稠密数组。
为什么扩展运算符能把空槽转成真实的
undefined 扩展运算符对数组做展开时,走的是数组的默认迭代器逻辑,这套逻辑的遍历规则和map类迭代方法完全不一样:
- 迭代器会严格绑定数组的
length属性,从索引0到length-1挨个读取位置值 - 不管这个位置有没有真实的属性键,读取操作返回的结果就是
undefined - 读取到的值会依次作为新数组的元素存入,新数组会为这些元素创建对应的真实索引键
所以[...Array(3)]的执行流程是:迭代器依次读取索引0、1、2的位置值,每次读出来都是undefined,把这三个值存入新数组后,新数组就拥有了真实的0/1/2键,键值为undefined,和手动声明的[undefined, undefined, undefined]没有任何区别。
补充:快速生成全固定值稠密数组的实用写法
如果需要快速生成长度为3、全为undefined的稠密数组,更直观的写法是用fill:
new Array(3).fill(undefined) // 返回[undefined, undefined, undefined]
因为fill方法的遍历规则是覆盖所有索引位置,不管位置上有没有真实的属性键,会直接把目标值写到对应键上,天然会把空槽替换成带真实键的元素。
内容的提问来源于stack exchange,提问作者dreadnaught
相关产品推荐
相关产品推荐

