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