Этот вопрос уже имеет ответ здесь:
Вот упрощенная версия чего-то, что я пытаюсь выполнить:
for (var i = 0; i < results.length; i++) {
marker = results[i];
google.maps.event.addListener(marker, 'click', function() {
change_selection(i);
});
}
но я нахожу, что каждый слушатель использует значение results.length (значение, когда для цикла завершается). Как я могу добавить слушателей, таким образом, что каждый использует значение меня в то время, когда я добавляю его, а не ссылка на меня?
В современных браузерах можно использовать ключевые слова let
или const
для создания переменной с блочной областью видимости:
for (let i = 0; i < results.length; i++) {
let marker = results[i];
google.maps.event.addListener(marker, 'click', () => change_selection(i));
}
В старых браузерах необходимо создать отдельную область видимости, которая сохраняет переменную в ее текущем состоянии, передавая ее как параметр функции:
for (var i = 0; i < results.length; i++) {
(function (i) {
marker = results[i];
google.maps.event.addListener(marker, 'click', function() {
change_selection(i);
});
})(i);
}
Создавая анонимную функцию и вызывая ее с переменной в качестве первого аргумента, вы передаете функцию по значению и создаете замыкание.
Помимо замыканий, вы можете использовать функцию .bind
:
google.maps.event.addListener(marker, 'click', change_selection.bind(null, i));
передает значение i
в качестве аргумента функции при вызове. ( null
предназначен для привязки this
, который вам не нужен в этом случае.)
function.bind
был введен платформой Prototype и стандартизирован в Пятое издание ECMAScript. Пока все браузеры не поддерживают его изначально, вы можете добавить свою собственную поддержку function.bind
, используя закрытие:
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner,
args.length===0? arguments : arguments.length===0? args :
args.concat(Array.prototype.slice.call(arguments, 0))
);
};
};
}
закрытия:
for (var i = 0, l= results.length; i < l; i++) {
marker = results[i];
(function(index){
google.maps.event.addListener(marker, 'click', function() {
change_selection(index);
});
})(i);
}
EDIT, 2013: Теперь они обычно называются IIFE
Вы завершаете закрытие. Вот статья о замыканиях и о том, как с ними работать. Посмотрите Пример 5 на странице; это сценарий, с которым вы имеете дело.
РЕДАКТИРОВАТЬ: Четыре года спустя эта ссылка мертва. Корень проблемы выше в том, что цикл for
формирует замыкания (особенно на marker = results [i]
). Поскольку маркер
передается в addEventListener
, вы видите побочный эффект закрытия: общая «среда» обновляется с каждой итерацией цикла, прежде чем она окончательно «сохраняется» через закрытие после последней итерации. MDN очень хорошо это объясняет.