如何避免从闭包访问可变variables
我有这样的代码:
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]]; ... }); }
我得到一个警告,在closures使用tmp_id
可能会导致问题,因为它是一个可变的variables。
我怎么能避免呢? 我的意思是我怎么能发送一个不可变的variablescallback,因为这是一个for循环,我不能改变zlib.gzip
代码? 换句话说,我怎么能把一个论点传递给一个封闭?
您需要创build一个范围,以使用自执行function正确捕获tmp_id
。 这是因为整个for循环是一个范围,意味着每次都是捕获相同的variables。 所以这个callback会以错误的ID结束,因为在callback被调用之前temp_id
的值会被改变。
我会忽略(或closures)警告,但是,似乎在抱怨,因为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]]; ... }); })(); }
我遇到了同样的问题,并通过将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); }
这里简单介绍user24359的很好的答案。 这是解决scheme:
var object = {a:1,b:2}; for (var y in object){ (function(){const yyy = y; setTimeout(function(){console.log(yyy)},3000);})(); }
上面的代码loggingab并且是解决scheme。 以下代码loggingbb:
var object = {a:1,b:2}; for (var y in object){ setTimeout(function(){console.log(y)},3000); }
我在量angular器中遇到了同样的问题。 解决它使用下面的代码 –
(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);
@ user24359答案是一个很好的解决scheme,但您可以简单地用let
关键字replacevar
关键字。
for(var id=0;
变
for(let id=0;
在这里看到细节。
for (var id = 0; id < message.receiver.length; id++) { var tmp_id = id; zlib.gzip(JSON.stringify(message.json), (err, buffer) => { // Something with tmp_id ... }); }
var
( tmp_id
)位于callback函数的上方作用域中创build闭包是一个常见的错误 ,应该避免这个错误 ,因为这个var
不是被作用域的。 正因为如此,并且由于循环中创build的每个闭包共享相同的词汇环境 ,所以在调用callback函数时,variables将始终是最后一次迭代值(即message.receiver.length-1
作为tmp_id
)。 您的IDE检测到这种行为,并抱怨正确。
为了避免这个警告,有几个解决scheme:
-
将
var
replace为let
(var
let tmp_id = id
)确保每个创build的闭包都有自己的在每个迭代中定义的tmp_id
:for (var id = 0; id < message.receiver.length; id++) { let tmp_id = id; zlib.gzip(JSON.stringify(message.json), (err, buffer) => { // Do something with tmp_id ... }); }
-
像gennadi.w一样利用IIEF创build每个迭代的词汇环境。
-
通过使用工厂函数(
createCallback
)在每个迭代中创buildcallback函数:const createCallback = tmp_id => (err, buffer) => { // Do something with tmp_id ... }; for (var id = 0; id < message.receiver.length; id++) { zlib.gzip(JSON.stringify(message.json), createCallback(id)); }
-
将variables
Bind
到它们所在的callback函数中: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)); }
注意:这只能在
function
s上使用。 箭头函数(=>
)不支持bind
。
通常,由于这种容易出错的行为,应该避免使用var
(截至ECMAScript 2015 )。