for in循环和删除操作符

时间:2012-08-21 18:30:15

标签: javascript

我注意到,在枚举对象的属性时,似乎是在循环开始时获取当前属性的快照,然后迭代快照。我觉得这样,因为以下内容不会创建无限循环:

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

demo http://jsfiddle.net/kqzLG/

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

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

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

demo http://jsfiddle.net/Gs2vh/

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

这里发生了什么? javascript是否具有它使用的某种类型的隐藏迭代器,并且delete运算符在某种程度上意识到它?

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

2 个答案:

答案 0 :(得分:7)

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

  

枚举属性的机制和顺序(第一个算法中的步骤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"]

答案 1 :(得分:1)

obj[i++] = 0;

第一次迭代:

  • 条目:i = 0,退出,i = 1

    第二次迭代:

  • 条目:i = 1,退出,i = 2

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