const a1 = [undefined, undefined, undefined];
const a2 = [, , ,];
a1.length // 3
a2.length // 3
a1[0] // undefined
a2[0] // undefined
a1[0] === a2[0] // true
上面代码中,数组 a1 的成员是三个 undefined,数组 a2 的成员是三个空位。这两个数组很相似,长度都是 3,每个位置的成员读取出来都是 undefined。
但是,它们实际上存在重大差异。
0 in a1 // true
0 in a2 // false
a1.hasOwnProperty(0) // true
a2.hasOwnProperty(0) // false
Object.keys(a1) // ["0", "1", "2"]
Object.keys(a2) // []
a1.map(n => 1) // [1, 1, 1]
a2.map(n => 1) // [, , ,]
上面代码一共列出了四种运算,数组a1和a2的结果都不一样。前三种运算(in运算符、数组的hasOwnProperty方法、Object.keys方法)都说明,数组a2取不到属性名。最后一种运算(数组的map方法)说明,数组a2没有发生遍历。
为什么a1与a2成员的行为不一致?数组的成员是undefined或空位,到底有什么不同?
数组成员可以省略。只要逗号前面没有任何表达式,数组的length属性就会加1,并且相应增加其后成员的位置索引。被省略的成员不会被定义。如果被省略的成员是数组最后一个成员,则不会导致数组length属性增加。
数组的空位会反映在length属性,也就是说空位有自己的位置,但是这个位置的值是未定义,即这个值是不存在的。如果一定要读取,结果就是undefined(因为undefined在JavaScript语言中表示不存在)。
这就解释了为什么in运算符、数组的hasOwnProperty方法、Object.keys方法,都取不到空位的属性名。因为这个属性名根本就不存在,规格里面没说要为空位分配属性名(位置索引),只说要为下一个元素的位置索引加1。