Как передать значение (а не ссылку) переменной JS в функцию?

Вот упрощенная версия того, что я пытаюсь запустить:

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', function() { 
        change_selection(i);
    }); 
}

но я обнаружил, что каждый слушатель использует значение results.length(значение, когда цикл for завершается). Как добавить слушателей таким образом, чтобы каждый из них использовал значение я в момент его добавления, а не ссылку на i?

Ответы

Ответ 1

В современных браузерах вы можете использовать ключевые слова 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);
}

Создавая анонимную функцию и вызывая ее с переменной в качестве первого аргумента, вы передаете значение функции и создаете закрытие.

Ответ 2

Как и замыкания, вы можете использовать function.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))
            );
        };
    };
}

Ответ 3

закрытия:

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

Ответ 4

Вы завершаетесь закрытием. Здесь статья о закрытии и о том, как с ними работать. Проверьте пример 5 на странице; что сценарий, с которым вы имеете дело.

EDIT: Четыре года спустя эта ссылка мертва. Корень проблемы выше состоит в том, что цикл for замыкает петли (в частности, на marker = results[i]). По мере того, как marker передается в addEventListener, вы видите побочный эффект закрытия: общая "среда" обновляется с каждой итерацией цикла, прежде чем он "наконец-то" будет "сохранен" через закрытие после последней итерации. MDN объясняет это очень хорошо.

Ответ 5

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', (function(i) {
        return function(){
            change_selection(i);
        }
    })(i)); 
}

Ответ 6

Я думаю, мы можем определить временную переменную для хранения значения i.

for (var i = 0; i < results.length; i++) {
 var marker = results[i];
 var j = i;
 google.maps.event.addListener(marker, 'click', function() { 
   change_selection(j);
 }); 
}

Я еще не тестировал его.