Как избежать доступа к изменяемой переменной из закрытия
У меня есть такой код:
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
? Или, другими словами, как я могу передать аргумент закрытию?
Ответы
Ответ 1
Вам нужно создать область для правильного захвата tmp_id
с помощью функции самоисполнения. Это потому, что весь цикл for является одной областью, означающей каждый раз, вы захватываете одну и ту же переменную. Таким образом, обратный вызов будет иметь неправильные идентификаторы, потому что значение 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]];
...
});
})();
}
Ответ 2
Я столкнулся с той же проблемой и решил немного изменить ответ user24359, передав id закрытию:
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
здесь упрощение user24359 отличного ответа.
Это решение:
var object = {a:1,b:2};
for (var y in object){
(function(){const yyy = y;
setTimeout(function(){console.log(yyy)},3000);})();
}
Вышеприведенный код регистрирует a и является решением.
Следующие журналы кода b b:
var object = {a:1,b:2};
for (var y in object){
setTimeout(function(){console.log(y)},3000);
}
Ответ 4
Я столкнулся с той же проблемой в транспортире. Решил его, используя следующий код -
(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 ответ - хорошее решение, но вы можете просто заменить ключевое слово var
ключевым словом let
.
for(var id=0;
становится
for(let id=0;
Подробнее здесь.
Ответ 6
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
), находящимся в верхней части функции обратного вызова, является распространенной ошибкой что следует избегать из-за того, что var
не является блочным. Из-за этого и потому, что каждое замыкание, созданное в цикле, имеет те же лексическую среду, переменная всегда будет последним итерированным значением (т.е. message.receiver.length-1
как tmp_id
) при вызове функции обратного вызова. Ваша IDE обнаруживает это поведение и жалуется правильно.
Чтобы избежать предупреждения, существует несколько решений:
-
Замените var
на let
( var
let tmp_id = id
), гарантируя каждому созданному закрытию свой собственный охваченный 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 ...
});
}
-
Создайте лексическую среду на каждой итерации, используя IIEF, например gennadi.w сделал.
-
Создайте функцию обратного вызова в каждой итерации с помощью функции factory (createCallback
):
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));
}
-
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));
}
Примечание: Это возможно только на function
s. Функции стрелок (=>
) не поддерживают Bind
.
Как правило, следует избегать var
(как из ECMAScript 2015) из-за такого поведения, подверженного ошибкам.