for ... in - 在进入循环之前,迭代对象只被评估一次吗?

时间:2016-02-15 20:16:34

标签: javascript language-lawyer

当我使用for ... in循环时:

for(var i in object) {}

我想知道有问题的object是仅评估一次还是每次循环,好吧,循环

我在浏览器(也在节点中)进行了快速测试,例如:

for(var i in (console.log('INIT'), [1, 2, 3, 4])) { console.log('KEY', i); }

我得到了:

INIT
KEY 0
KEY 1
KEY 2
KEY 3

因此,根据这些经验证据,我可以假设确实只评估过一次。

但是,这种行为标准吗?

3 个答案:

答案 0 :(得分:3)

从Mozilla documentation开始,for...in循环将遍历对象本身的所有可枚举属性以及对象从其构造函数的原型继承的属性。在访问之前删除的(可枚举)属性以后将不会被访问。添加到正在进行迭代的对象的属性可以在迭代中被访问或省略。

简而言之,虽然Chrome和IE可能会对for ..in循环使用不同的规范,但@Amit发布的示例的结果并不能保证。但是,至少删除一个元素似乎阻止了Chrome访问它:

var obj = { a: 1, b: 2, c: 3 };

for(var k in obj) {
  document.write(k);
  delete obj['c']; 
}

答案 1 :(得分:1)

请记住,(console.log('INIT'), [1, 2, 3, 4])是一个评估为[1, 2, 3, 4]的表达式,因此它不是问题的有效证据。可以通过以下方式获得更好的经验证据:

var obj = { a: 1, b: 2, c: 3 };

for(var k in obj) {
  document.write(k);
  obj.d = 4;
}

我们没有看到“d”......

答案 2 :(得分:0)

ECMAScript® Language Specification, section 12.6.4对此并没有太多说明,只是右侧的表达式被评估一次并转换为对象:

  
      
  1. exprRef 成为评估 Expression 的结果。
  2.   
  3. experValue 成为GetValue( exprRef )。
  4.   
  5. 如果 experValue nullundefined,则返回(正常,空,空)。
  6.   
  7. obj 成为ToObject( experValue )。
  8.   

这对于评估当时该对象的任何键或值没有任何说明。

实际上,引用段落后面的部分表明可以在迭代期间检索密钥:

  
      
  1. 重复      
        
    • P obj 的下一个属性的名称,其[[Enumerable]]属性为true。如果没有这样的属性,请返回(正常, V ,空)。
    •   
  2.   

但两个方向都没有要求。所以,......这可能是实现(浏览器)依赖。

当您说对象只评估一次时,可能会有不同的含义:

  1. 对象引用仅评估一次。当该对象是表达式的结果(例如函数调用)时,这很重要。但这肯定是理所当然的,而不是你的意思;
  2. 对象的(属性)枚举一次,但会根据需要检索值;
  3. 检索对象的键和值一次。这可以在1级或嵌套级别上以潜在的高内存成本完成。
  4. 此测试用例在我的浏览器(FireFox)中显示第二个发生,而不是第三个:

    var obj = {a: 1, b: 2, c: 3};
    for (key in obj) {
        document.write('obj[' + key + '] = ' + obj[key] + '<br>');
        if (key=='a') {
            // modify a value that is still to be visited
            obj["c"] = 4;
            // add a key/value pair
            obj["d"] = 9;
        }
    }

    输出(您可能也会在浏览器中看到):

      

    obj [a] = 1
      obj [b] = 2
      obj [c] = 4

    所以密钥只被检索一次,按需查询值(即不在循环开始时)。