32

我有一些这样的代码:

for(var id=0; id < message.receiver.length; id++){
   var tmp_id = id;
   zlib.gzip(JSON.stringify(message.json), function(err, buffer){
                        ...
   pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
   delete pushStatusPool[message.receiver[tmp_id]];
   ...
   });
}

我得到一个警告,tmp_id在闭包中使用可能会导致问题,因为它是一个可变变量。

我怎么能避免呢?我的意思是我怎么能将一个不可变的变量发送到回调,因为这是一个 for 循环,我不能更改代码zlib.gzip?或者换句话说,我如何将参数传递给闭包?

4

6 回答 6

41

您需要创建一个范围以tmp_id使用自执行功能正确捕获。那是因为整个 for 循环是一个作用域,这意味着每次通过时,您都在捕获相同的变量。所以回调会以错误的 id 结束,因为temp_id' 的值会在回调被调用之前被改变。

不过,我会忽略(或关闭)警告,这似乎是在抱怨,因为temp_id它是可变的,你可能会重新分配它。这有点傻。如果您真的想修复它,请尝试使用const关键字而不是var.

for(var id=0; id < message.receiver.length; id++){
   (function(){
       const tmp_id = id;
       zlib.gzip(JSON.stringify(message.json), function(err, buffer){
                        ...
           pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
           delete pushStatusPool[message.receiver[tmp_id]];
           ...
       });
   })();
}
于 2012-12-11T07:15:25.057 回答
11

我遇到了同样的问题,并通过将 id 传递给闭包来稍微修改 user24359 的答案来解决它:

for(var id=0; id < message.receiver.length; id++){
   (function(tmp_id){
       zlib.gzip(JSON.stringify(message.json), function(err, buffer){
                        ...
           pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
           delete pushStatusPool[message.receiver[tmp_id]];
           ...
       });
   })(id);
}
于 2013-08-21T14:05:19.513 回答
2

这里简化了 user24359 的好答案。这是解决方案:

var object = {a:1,b:2};

for (var y in object){
    (function(){const yyy = y;
        setTimeout(function(){console.log(yyy)},3000);})();
}

上面的代码记录 ab 并且是解决方案。以下代码记录 bb :

var object = {a:1,b:2};
for (var y in object){

    setTimeout(function(){console.log(y)},3000);
}
于 2014-03-06T21:40:20.910 回答
0

我在量角器中遇到了同样的问题。使用以下代码解决了它 -

(function(no_of_agents){
              ptor.element.all(by.repeater('agent in agents').column('displayName')).then(function(firstColumn){
                    console.log(i, '>>>>>Verifying the agent Name');
                    var agentsSorted = sortAgentsByName();
                    //verify the agent name
                    expect(firstColumn[no_of_agents].getText()).toEqual(agentsSorted[no_of_agents].name);
                    //now click on the agent name link
                    firstColumn[no_of_agents].click();
                    ptor.sleep(5000);
              });
            })(no_of_agents);
于 2014-03-11T10:00:38.893 回答
0

@user24359 答案是一个很好的解决方案,但您可以简单地用关键字替换var关键字let

for(var id=0;

变成

for(let id=0;

在此处查看详细信息。

编辑:正如 Heriberto Juárez 建议的那样,它只适用于支持 EcmaScript6 的浏览器。

于 2017-01-25T13:47:48.700 回答
0

var在循环中使用( tmp_id) 位于回调函数的上层范围内创建闭包是一个常见错误var,由于不是块作用域,应该避免这种错误。正因为如此,并且因为在循环中创建的每个闭包都共享相同的词法环境,所以当调用回调函数时,该变量将始终是最后一个迭代值(即message.receiver.length - 1as )。tmp_id您的 IDE 检测到这种行为并正确地抱怨。

为避免警告,有几种解决方案:

  • 替换var为确保每个创建的闭包在每次迭代中定义let自己的范围:tmp_id

    for (var id = 0; id < message.receiver.length; id++) {
      let tmp_id = id;
      zlib.gzip(JSON.stringify(message.json), function(err, buffer) {
        // Do something with tmp_id ...
      });
    }
    
  • 通过像gennadi.w 那样利用IIFE在每次迭代中创建一个词法环境。

  • 使用工厂函数 ( ) 在每次迭代中创建回调函数createCallback

    const createCallback = tmp_id => function(err, buffer) {
      // Do something with tmp_id ...
    };
    for (var id = 0; id < message.receiver.length; id++) {
      zlib.gzip(JSON.stringify(message.json), createCallback(id));
    }
    
  • bind回调函数上的变量,它们被添加到其参数中:

    for (var id = 0; id < message.receiver.length; id++) {
      zlib.gzip(JSON.stringify(message.json), function(tmp_id, err, buffer) {
        // Do something with tmp_id (passed as id) ...
      }.bind(this, id));
    }
    

如果可能的话,var从 ECMAScript 2015 开始应该避免,因为这种容易出错的行为。

于 2017-06-27T10:36:23.570 回答