在 JavaScript 的第一个版本中,没有数组。它们后来作为“所有对象之母”Object的子类引入。你可以通过执行以下操作轻松测试这一点:
var foo = [1,2,3,4];
for (var n in foo)
{
console.log(n === +(n) ? 'Number' : 'String');
}
这将会记录
String
,一遍又一遍。内部所有数字键都会被转换为字符串。Length 属性仅获取最高索引,并加 1。没有更多的操作。当显示数组时,对象被迭代,对于每个键,与任何对象相同的规则适用: 首先扫描实例,然后是原型... 所以如果我们稍微改变一下代码:
var foo = [1,2,3,4];
foo[9] = 5;
for (var n in foo)
{
if (foo.hasOwnProperty(n))
{
console.log(n === +(n) ? 'Number' : 'String');
}
}
你会注意到这个数组只有5个自身属性,键为undefined
的4-8是未定义的,因为在实例中没有找到对应的值,也没有在任何底层原型中找到。简而言之:数组并不是真正的数组,而是表现类似的对象。
正如Tim所说,你可以有一个包含未定义属性的数组实例,该属性确实存在于该对象中:
var foo = [1,2,undefined,3]
console.log(foo[2] === undefined)
console.log(foo[99] === undefined)
不过,这里仍然有一个区别:
console.log((foo.hasOwnProperty('2') && foo[2] === undefined))
console.log((foo.hasOwnProperty('99') && foo[99] === undefined))
回顾,你的三个主要问题:
更新:
仅引用Ecma std:
15.4数组对象
数组对象会对某些类别的属性名给予特殊处理。一个属性名P(以字符串值的形式)仅在ToString(ToUint32(P))等于P且ToUint32(P)不等于2^32
1时是数组索引。名称为数组索引的属性也称为元素。每个数组对象都有一个length属性,其值始终是小于2 ^ 32的非负整数。length的值
属性在名称为数组索引的每个属性的名称大于其名称时数值上均大于;无论何时添加其名称为数组索引的属性,如有必要,则更改length
属性,使其比该数组索引的数字值大1; 每当更改length时
属性,自动删除其值不小于新长度的每个名称为数组索引的属性。此约束仅适用于Array对象的自有属性,并且不受其原型继承的length或数组索引属性的影响。
如果以下算法返回true,则称对象O稀疏:
1. 让len成为使用参数“ length”调用O的[[Get]]内部方法的结果。
2. 对于范围为0≤i的每个整数i
a. 允许elem使用参数调用O的[[GetOwnProperty]]内部方法 ToString(i)。
b. 如果未定义elem,请返回true。
3. 返回false。