为什么我添加新属性时数组的长度不会改变?

7

var arr = ["Hello", "There", 123, 456, {
    show: function (value) {
        alert(value);
    }
}];
arr[4].show(arr[0]);
arr["Hello"] = {
    damn: function () {
        alert("What's happening yo !");
    }
}
arr.Hello.damn();
alert("Arr length is: " + arr.length);


4
数组应该只有数值键。任何非数值键不会计入长度。 - elclanrs
4个回答

11

引用ECMA Script 5规范中的Array Objects部分:

如果属性名P(以字符串形式)是一个数组索引,那么当且仅当ToString(ToUint32(P))等于PToUint32(P)不等于232−1时,它就是一个数组索引。

根据上述定义,由于Hello无效,因此它不被视为数组索引,而只是普通属性。

引用MDN的Relationship between length and numerical properties section部分:

当在JavaScript数组上设置属性时,如果该属性是有效的数组索引并且该索引超出了数组的当前范围,则引擎将相应地更新数组的长度属性

因此,只有当属性是有效的数组索引时,length属性才会被调整。

在您的情况下,您刚刚在数组对象上创建了一个新属性Hello
注意:在所有的Array原型函数中,只有数值属性会被使用,例如forEach、map等。
例如,在问题中展示的数组,当与forEach一起使用时,
arr.forEach(function(currentItem, index) {
    console.log(currentItem, index);
})

将会打印
Hello 0
There 1
123 2
456 3
{ show: [Function] } 4

尽管键列表显示 Hello
console.log(Object.keys(arr));
// [ '0', '1', '2', '3', '4', 'Hello' ]

这是因为Array是从Object派生而来的。

console.log(arr instanceof Object);
// true

Hello 是一个数组对象中有效的键,但不是有效的数组索引。因此,当您将数组视为对象时,Hello 将包含在键中,但特定于数组的函数将仅包括数字属性。


是的!如果将值保存为“关联数组”样式,则长度不会增加。 - Sandeep

1
这是因为只有在添加新的数字属性到数组中时,length 才会更新 根据规范要求

该数组对象的 length 属性是一个数据属性,其值始终大于每个可删除属性的名称,其名称是数组索引。

而数组索引 被指定为

如果且仅当 ToString(ToUint32(P)) 等于 P 且 ToUint32(P) 不等于 232−1 时,属性名 P(以字符串值形式)是一个数组索引。

然而,数组也是对象 - 因此您可以像向任何其他 JavaScript 对象添加属性一样,向数组添加非数组索引属性(这就是为什么您的示例也不会抛出异常的原因)。


1

JavaScript的长度是根据1 +(最高数字索引元素)计算的。因此,当您添加arr ['Hello']时,您只添加了一个字符串索引,这在计算数组长度时不予考虑。

这是ECMAScript 5.1中描述的数组长度属性的实际定义:

每个数组对象都有一个长度属性,其值始终是一个小于232的非负整数。长度属性的值在数字上大于每个以数组索引为名称的属性的名称;无论何时创建或更改数组对象的属性,其他属性都会根据需要进行调整,以保持这个不变式。具体来说,每当添加一个以数组索引为名称的属性时,如果需要,长度属性将被更改为该数组索引的数值加一;并且每当更改长度属性时,所有名称为数组索引且值不小于新长度的属性都将自动删除。此约束仅适用于数组对象的自有属性,并不受可能从其原型继承的长度或数组索引属性的影响。

0

当你写下:

arr["Hello"]=...

你正在创建一个与arr数组对象相关联的新对象,这不会影响arr的长度。

你可以通过写下以下代码来实现相同的效果:

arr.Hello=...


不行,你不能这样做。结果将是相同的,arr.length 为0。 - Arif

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接