添加新属性时,为什么数组的长度没有变化?

问题描述 投票:7回答:4

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);
javascript arrays
4个回答
9
投票

引用ECMAScript 5规范Array Objects

当且仅当P等于ToString(ToUint32(P))P不等于232-1时,属性名称ToUint32(P)(以String值的形式)是数组索引。

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

引用MDN的Relationship between length and numerical properties section

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

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

在您的情况下,您刚刚在数组对象上创建了一个新属性Hello


注意:只有数字属性才会用在所有Array的原型函数中,如forEachmap等。

例如,当与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将包含在键中,但特定于数组的函数将仅包含数字属性。


1
投票

发生这种情况是因为length仅在将新的数字属性添加到数组as required by the specification时更新:

此Array对象的length属性是一个data属性,其值始终在数字上大于名称为数组索引的每个deletable属性的名称。

和数组索引is specified to be

当且仅当ToString(ToUint32(P))等于P且ToUint32(P)不等于232-1时,属性名P(以String值的形式)是数组索引。

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


0
投票

javascript长度计算为1+(最高数字索引元素)。因此,当您添加arr['Hello']时,您只需添加一个字符串索引,在计算数组长度时不会将其考虑在内。

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

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


0
投票

当你写:

ARR [ “你好”] = ...

您正在创建一个与arr Array对象关联的新Object,它不会影响arr的长度。

您可以通过编写来实现相同的效果:

arr.Hello = ...

© www.soinside.com 2019 - 2024. All rights reserved.