for in循环和删除操作符

goa*_*oat 11 javascript

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

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

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

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

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

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

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

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

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

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

Fel*_*ing 7

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

枚举属性的机制和顺序(第一个算法中的步骤6.a,第二个算法中的步骤7.a)未指定.枚举期间可以删除要枚举的对象的属性.如果删除枚举期间尚未访问的属性,则不会访问该属性.如果在枚举期间将新属性添加到要枚举的对象,则无法保证在活动枚举中访问新添加的属性.在任何枚举中不得多次访问属性名称.

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

例如,在Chrome中,似乎数字属性按字母顺序存储:

> Object.keys({a:0, 0:1});
  ["0", "a"]
Run Code Online (Sandbox Code Playgroud)

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

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

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

Firefox显示相同的行为,尽管密钥以插入顺序存储:

> Object.keys({a:0, 0:1});
  ["a", "0"]
Run Code Online (Sandbox Code Playgroud)