在 JavaScript 的第一个版本中,没有数组。它们后来作为“所有对象之母”的子类引入:Object。您可以通过以下方式轻松测试:
var foo = [1,2,3,4];
for (var n in foo)
{//check if n is equal (value and type) to itself, coerced to a number
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))
{//check if current key is an array property
console.log(n === +(n) ? 'Number' : 'String');
}
}
您会注意到该数组只有 5 个自己的属性,undefined 键 4-8 未定义,因为在实例中没有找到对应的值,在任何底层原型中也没有。简而言之:数组并不是真正的数组,而是行为相似的对象。
正如 Tim 所说,您可以拥有一个数组实例,该实例具有未定义的属性,确实存在于该对象中:
var foo = [1,2,undefined,3];
console.log(foo[2] === undefined);//true
console.log(foo[99] === undefined);//true
但还是有区别的:
console.log((foo.hasOwnProperty('2') && foo[2] === undefined));//true
console.log((foo.hasOwnProperty('99') && foo[99] === undefined));//false
回顾一下,你的三个主要问题:
更新:
只要quoting the Ecma std:
15.4 数组对象
数组对象对某一类属性名称给予特殊处理。属性名称 P(形式为
String value) 是一个数组索引当且仅当 ToString(ToUint32(P)) 等于 P 并且 ToUint32(P) 不等于
2^32
1。属性名称为数组索引的属性也称为元素。每个 Array 对象都有一个
length 属性,其值始终是小于 2^32 的非负整数。长度值
property 在数值上大于名称为数组索引的每个属性的名称;每当一个
创建或更改 Array 对象的属性,根据需要调整其他属性以保持此
不变的。具体来说,每当添加名称为数组索引的属性时,长度属性为
如有必要,更改为比该数组索引的数值大一;并且每当长度
属性被更改,每个属性名称是一个数组索引,其值不小于新的
长度被自动删除。此约束仅适用于 Array 对象的自身属性,并且是
不受可能从其原型继承的长度或数组索引属性的影响。
如果以下算法返回 true,则称对象 O 为稀疏对象:
1. 设 len 为调用 O 的 [[Get]] 内部方法的结果,参数为“length”。
2.对于0≤i范围内的每个整数i
一个。令 elem 为带参数调用 O 的 [[GetOwnProperty]] 内部方法的结果
ToString(i)。
b.如果 elem 未定义,则返回 true。
3. 返回false。