可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have some code like this:
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]];
...
});
}
And I got a warning that using tmp_id
in closure may cause problem because it is a mutable variable.
How could I avoid that? I mean how could I send an immutable variable to callback since this is a for loop and I can not change code of zlib.gzip
? Or in other words, how could I pass a argument to a closure?
回答1:
You need to create a scope to correctly capture tmp_id
using a self-executing function. That's because the entire for loop is one scope, meaning each time through, you're capturing the same variable. So the callback will end up with the wrong ids, because temp_id
's value will get changed before the callback is called.
I'd ignore (or shut off) the warning, though, which seems to be complaining that because temp_id
is mutable, you might reassign it. That's sort of silly. If you really want to fix it, try using the const
keyword instead of 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]];
...
});
})();
}
回答2:
I have faced the same problem and solved it slightly modifying the answer of user24359, by passing the id to the closure:
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);
}
回答3:
here a simplification of user24359's great answer.
This is the solution:
var object = {a:1,b:2};
for (var y in object){
(function(){const yyy = y;
setTimeout(function(){console.log(yyy)},3000);})();
}
The above code logs a b and is the solution.
The following code logs b b :
var object = {a:1,b:2};
for (var y in object){
setTimeout(function(){console.log(y)},3000);
}
回答4:
I've faced the same problem in protractor. Solved it using following code -
(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);
回答5:
@user24359 answer is a good solution but you can simply replace the var
keyword by the let
keyword.
for(var id=0;
becomes
for(let id=0;
See details here.
Edit : As Heriberto Juárez suggested it, it will only works for browsers that supports EcmaScript6.
回答6:
Creating closures in a loop with var
(tmp_id
) being in the upper scope of the callback function is a common mistake that should be avoided due to the var
not being block-scoped. Because of this, and because each closure, created in the loop, shares the same lexical environment, the variable will always be the last iterated value (i.e. message.receiver.length - 1
as tmp_id
) when the callback function gets invoked. Your IDE detects this behavior and complains rightly.
To avoid the warning, there are several solutions:
Replace var
with let
ensuring each created closure to have its own scoped tmp_id
defined in each iteration:
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 ...
});
}
Create a lexical environment in each iteration by leveraging IIFE like gennadi.w did.
Create a callback function in each iteration by using a factory function (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
the variable(s) on the callback function in which they get prepended to its parameters:
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));
}
If possible, var
should be avoided as of ECMAScript 2015 due to such error-prone behaviors.