10

我注意到在枚举对象的属性时,似乎在循环开始时拍摄了当前属性的快照,然后迭代快照。我有这种感觉是因为以下内容不会造成无限循环:

var obj = {a:0,b:0}, i=0;
for (var k in obj) {
    obj[i++] = 0;
}
alert(i) // 2

演示http://jsfiddle.net/kqzLG/

上面的代码演示了我正在添加新属性,但不会枚举新属性。

然而,删除操作符似乎违背了我的快照理论。这是相同的代码,但在枚举之前删除了一个属性。

var obj = {a:0,b:0}, i=0;
for (var k in obj) {
    i++;
    delete obj.b;
}
alert(i) // 1

演示http://jsfiddle.net/Gs2vh/

上面的代码演示了循环体只执行了一次。如果快照理论是真的,它会执行两次。

这里发生了什么?javascript 是否有某种类型的隐藏迭代器,它使用,并且删除操作员不知何故知道它?

——我意识到我假设了一些关于迭代顺序的东西——特别是迭代是基于属性插入时间发生的。我相信所有浏览器都使用这样的实现。

4

2 回答 2

7

有趣的问题。答案在于规范(强调我的):

枚举属性的机制和顺序(第一个算法中的步骤 6.a,第二个算法中的步骤 7.a)未指定。被枚举对象的属性可能在枚举过程中被删除。如果在枚举期间还没有访问过的属性被删除,那么它就不会被访问。如果在枚举过程中向正在枚举的对象添加了新属性,则不保证在活动枚举中访问新添加的属性。在任何枚举中不能多次访问属性名称。

因此明确规定不能再遍历已删除的属性。但是,添加新属性的行为取决于实现,很可能是因为它没有定义属性应如何在内部存储。

例如,在 Chrome 中,数字属性似乎存储在字母属性之前:

> Object.keys({a:0, 0:1});
  ["0", "a"]

但是,即使您添加字母键:

var obj = {a:0,b:0};
for (var k in obj) {
    obj['c'] = 0;
    console.log(k);
}

c似乎没有被遍历,输出是a b.

Firefox 显示相同的行为,尽管键是按插入顺序存储的:

> Object.keys({a:0, 0:1});
  ["a", "0"]
于 2012-08-21T18:34:11.360 回答
1

obj[i++] = 0;

第一次迭代:

  • 进入:i = 0,退出,i = 1

    第二次迭代:

  • 入口:i = 1,出口,i = 2

    此外,在迭代对象时,不能保证 javascriptfor in循环按顺序完成。因此,delete obj.b;会产生不可预测的结果。

  • 于 2012-08-21T18:33:12.897 回答