我注意到,在枚举对象的属性时,似乎是在循环开始时获取当前属性的快照,然后迭代快照。我觉得这样,因为以下内容不会创建无限循环:
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运算符在某种程度上意识到它?
- 我意识到我正在假设一些关于迭代顺序的东西 - 特别是迭代是基于属性插入时间发生的。我相信所有浏览器都使用这样的实现。
答案 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;
第一次迭代:
第二次迭代:
此外,在迭代对象时,不保证按顺序完成javascript for in
循环。因此,delete obj.b;
会产生不可预测的结果。