空位不是undefined或null,而是索引根本不存在;map等高阶方法基于属性存在性,跳过空位不执行回调,而join、for循环等基于索引连续性,将空位视作undefined处理。

数组“空位”(holes)不是 undefined,也不是 null,而是索引位置**根本不存在**——它是一种稀疏性语法现象。这种“缺失”在不同数组方法中触发完全不同的底层逻辑,关键区别在于:方法是基于「属性存在性」工作,还是基于「索引连续性」工作。
map 会跳过空位,不执行回调
map(以及 forEach、filter、reduce 等高阶函数)严格遵循 ECMAScript 规范的“稀疏数组语义”:只对「实际拥有可枚举属性的索引」调用回调函数。空位没有属性,因此被完全忽略。
-
[1, , 3].map(x => x * 2)→[2, 6](长度从 3 变为 2) - 回调函数根本不会收到空位位置的任何调用,
console.log在中间不会输出 - 注意:
arr[1] = undefined是一个真实存在的undefined值,map会处理它;而空位[1, , 3]的索引1连1 in arr都是false
join 会把空位当作 undefined 处理,输出空字符串
join 不关心属性是否存在,它按索引顺序(0 到 length - 1)拼接每个位置的值。遇到空位时,内部调用 ToString(undefined),结果是空字符串 ""。
-
[1, , 3].join('|')→"1||3"(中间两个竖线之间是空字符串) -
[, , 'a'].join('-')→"-a"(前两个空位都转为空字符串,连成"--a"?不对——其实是"" + "-" + "" + "-" + "a"→"--a") - 这和
for循环或for...of行为一致:它们都“看到”索引位置,只是值读出来是undefined
对比其他常见方法,看清行为分组
理解差异不能只记单个方法,要归类看逻辑根源:
-
跳过空位组(基于属性存在性):
map、forEach、filter、some、every、find、Object.keys、for...in -
视为空位为 undefined 组(基于索引遍历):
join、toString、for循环、for...of、Array.from、扩展运算符([...arr]) -
序列化特殊组:
JSON.stringify把空位转成null([1,,3]→"[1,null,3]")
如何快速检测一个位置是不是空位
不能靠 arr[i] === undefined,因为真实 undefined 值也会满足这个条件。必须用存在性检测:
-
i in arr→false表示是空位(推荐) -
arr.hasOwnProperty(i)→ 同样返回false -
Object.prototype.propertyIsEnumerable.call(arr, i)→ 也返回false - 注意:
arr[i] === undefined && !(i in arr)才能稳妥断定是空位










