Array.pop()会改变数组的长度吗?

6
我正在ideone尝试以下代码:
var a = [];
a[0] = 0;
a[5] = 5;
a[6] = undefined;
print("contents before popping:");
for(var e in a) print("\ta[", e, "] =", a[e]);
print("a.length =", a.length);
for (var i = 0; i < a.length; i++) 
    print("\ta.hasOwnProperty(", i, ") =", a.hasOwnProperty(i));

print("popping -->", a.pop());
print("popping -->", a.pop());
print("contents after popping:");
for(var e in a) print("\ta[", e, "] =", a[e]);
print("a.length =", a.length);
for (var i = 0; i < a.length; i++) 
    print("\ta.hasOwnProperty(", i, ") =", a.hasOwnProperty(i));

输出结果如下:
contents before popping:
    a[ 0 ] = 0
    a[ 5 ] = 5
    a[ 6 ] = undefined
a.length = 7
    a.hasOwnProperty( 0 ) = true
    a.hasOwnProperty( 1 ) = false
    a.hasOwnProperty( 2 ) = false
    a.hasOwnProperty( 3 ) = false
    a.hasOwnProperty( 4 ) = false
    a.hasOwnProperty( 5 ) = true
    a.hasOwnProperty( 6 ) = true
popping --> undefined
popping --> 5
contents after popping:
    a[ 0 ] = 0
a.length = 5
    a.hasOwnProperty( 0 ) = true
    a.hasOwnProperty( 1 ) = false
    a.hasOwnProperty( 2 ) = false
    a.hasOwnProperty( 3 ) = false
    a.hasOwnProperty( 4 ) = false

在我的 JavaScript 书(嗯,是 Crockford 的)中,我读到数组的长度是由所有属性中最大的数值计算出来的。那么,当它最大的数值属性为 0 时,为什么它的长度是 5?谢谢!
4个回答

6
Javascript的一个好处是它的语义在标准中被正式且尽可能明确地定义。具体来说,第15.4节指出:
“长度属性的值大于每个名称为数组索引的属性的名称;每当创建或更改Array对象的属性时,将根据需要调整其他属性以维护此不变量。”
换句话说,每次更改数组时,它的length会被调整,但只保证大于max(indexes),而不一定等于max(indexes)+1。例如,pop方法总是删除length-1元素并将length减一,无论数组实际包含多少元素。
请注意,大多数数组方法是通用的,并不特别了解数组。它们可以操作任意对象,如果这样的对象恰好有一个名为length的属性,则会根据上述规则进行调整。请考虑:
foo = {
    length: 100,
    99: 'hi'
}

alert([].pop.apply(foo)) // hi
alert(foo.length)        // 99

alert([].pop.apply(foo)) // undefined
alert(foo.length)        // 98

在您的问题背景下,数组的具体表示方式并不重要 - pop等方法不使用此信息。

我一直在传递这个小提琴,以演示Array.length不可靠的情况,除了你的例子之外。http://jsfiddle.net/6WrkT/1/ - dev_row

1
"问题"是分配5任务
一旦这样做,Javascript数组就会被填满插槽,并用undefined值初始化。
因此调用
a[5] = 5;

原因
[0, undefined, undefined, undefined, undefined, 5]

然后您还需要创建a[6]并将其初始化为undefined,我们就会得到:
[0, undefined, undefined, undefined, undefined, 5, undefined]

.length7 的长度。

现在我们调用一个双倍.pop()(喜欢这个词)然后我们得到:

[0, undefined, undefined, undefined, undefined]

.length 的值是 5


实际上,正如评论中指出的那样,这仍然似乎是非常奇怪的行为。当初始化任何索引时,前面的插槽并没有真正地被任何东西赋值。它创建了一个sparse array。但是,如果我们删除最后两个条目,则只剩下一个值(0),后面跟着undefined值(一些不知何故变成了真实值,可能是因为我们手动分配了一个6)。

我想我们需要一个实现大师:)


2
数组没有填充,因为hasOwnProperty返回false - Roman Saveljev
我不确定中间值是否初始化为“undefined”,我认为“a”是一个稀疏数组。 - lmsteffan
@lmsteffan 你说得对,我现在自己都有点糊涂了。看起来我的描述是正确的,但实际上不应该有任何剩余的插槽。 - jAndy
我可以想象解释可能是正确的,但浏览器区分“自动填充”的未定义和明确设置的未定义。 - Ingo Bürk
区分是否计算为“hasOwnProperty”。但这似乎是一个非常大的怪癖,甚至可能是一个错误。这在所有浏览器中都表现相同吗? - Ingo Bürk
显示剩余3条评论

1

数组的长度由所有属性中最大的数值计算得出。

但并非所有情况下都适用于数组。如果您为数组中的索引分配一个值,则其工作方式如此,但如果您特别设置了length或使用pop等方法,则不适用。

例如:

var a = [];
a.length = 5;

console.log("length =", a.length);

for (var i = 0; i < a.length; i++) 
  console.log("a.hasOwnProperty(", i, ") =", a.hasOwnProperty(i));

输出:

length = 5
a.hasOwnProperty(0) = false
a.hasOwnProperty(1) = false
a.hasOwnProperty(2) = false
a.hasOwnProperty(3) = false
a.hasOwnProperty(4) = false

现在你有一个没有任何元素的数组,但长度为5。

但问题仍然存在,为什么和在哪里成为插槽1...4真实插槽,具有undefined值?实际上,正如@lamsteffan正确指出的那样,我们只是处理一个稀疏数组。 - jAndy
@jAndy,我猜Guffa是对的。Array.splice和直接赋值length表现出相同的行为,原因似乎真的是在某个时刻分配了length - Roman Saveljev
@jAndy:未使用的插槽没有任何值,但如果您尝试从不存在的插槽读取,则会返回undefined。例如console.log([][42]);将显示undefined - Guffa
@Guffa 我知道。但是我很困惑,因为在我们弹出最后两个值之后,长度仍然为“5”。什么时候会被标记 .length?嗯,我猜是在赋值时,然后它再也不会被重置...好的,这很有道理 :p - jAndy
3
pop 方法不考虑空槽,它获取根据 length 属性确定的最后一项并将 length 减一。甚至可以在具有 length 属性的任何对象上应用 pop 方法,该对象甚至不必是数组:http://jsfiddle.net/4kucg/ - Guffa
@Guffa 哎呀,那太虚张声势了。 - jAndy

0
问题实际上与您未使用顺序索引有关,而不是与您使用pop函数有关。您会注意到,在第一个长度打印时,数组的长度为7,尽管数组中只有3个项目。这与长度返回最大索引/属性(+1)有关,因为其余的值被设置为未定义的值。
您真的必须使用非顺序索引吗?如果是这样,您可以尝试使用:
var counter = 0;
for (var i in a) {
    counter++;
  print("Property: "+i);
}
print("Total items: "+counter);

但从性能角度来看,这远非最优解。


你好,我的代码没有任何问题。这只是人工构造的,我只是想理解这种语言。我正在寻找为什么它以这种方式工作的解释,而不是如何修复它。 - Roman Saveljev
理解了。那我必须同意上面的答案,尽管我不确定在所有浏览器中是否是相同的行为。 - sagibb

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