Передать локальную переменную функции обратного вызова

Вопрос

Как функция обратного вызова сохраняет локальную переменную, откуда она была создана?

Простой пример

Я создаю видеоплеер. Он будет иметь ползунки для управления насыщенностью, контрастностью и оттенком. Когда пользователь играет с ползунками, он должен признать, какой слайдер был изменен и какое значение он изменил. Проблема заключается в том, что имя слайдера является локальной переменной из области создателя этого обратного вызова onChange. Как этот обратный вызов может сохранить имя ползунка?

HTML

<div id="saturation">
 <div class="track"></div>
  <div class="knob"></div>
 </div>
</div>

<div id="contrast">
 <div class="track"></div>
  <div class="knob"></div>
 </div>
</div>

<div id="hue">
 <div class="track"></div>
  <div class="knob"></div>
 </div>
</div>

JS

var elements = [
 'saturation',
 'contrast',
 'gamma'
];

for(var i = 0; i < sliders.size(); i++) {
 new Control.Slider(
  $(elements[i]).down('.knob'),
  $(elements[i]).down('.track'), {
   onChange: function(value) {
    // ERROR: elements[i] is undefined
    alert(elements[i] + ' has been changed to ' + value);
   }
 }
}

Ответ 1

Та же переменная, i - значение которого заканчивается как 4 - привязано к каждой функции, которую вы создаете внутри цикла. Вы можете обернуть функцию в другую функцию, которую вы вызываете на месте, и передать i в качестве параметра этой функции:

for(var i = 0; i < sliders.size(); i++) {
 new Control.Slider(
  $(elements[i]).down('.knob'),
  $(elements[i]).down('.track'), {
   onChange: (function(inner_i) { function(value) {
    alert(elements[inner_i] + ' has been changed to ' + value);
   } })(i)
 }
}

Ответ 2

Создайте копию переменной для каждого обратного вызова, вы можете сделать это с помощью анонимной функции, которую вы передадите в значение:

for(var i = 0; i < sliders.size(); i++) {

    (function(e) { // get a local copy of the current value

        new Control.Slider(
          $(elements[e]).down('.knob'),
          $(elements[e]).down('.track'), {
           onChange: function(value) {
            // ERROR: elements[e] is undefined
            alert(elements[e] + ' has been changed to ' + value);
           }
         }

     })(i); // pass in the current value
}

Таким образом, вы не ссылаетесь на те же самые i X.

Ответ 3

Поместите свою функцию в закрытие таким образом:


for(var i = 0; i < sliders.size(); i++) {
 (function(q){
 new Control.Slider(
  $(elements[q]).down('.knob'),
  $(elements[q]).down('.track'), {
   onChange: function(value) {
    // ERROR: elements[q] is undefined
    alert(elements[q] + ' has been changed to ' + value);
   }
 }
 })(i)
}