Как передать переменную по значению в анонимную функцию JavaScript? - PullRequest
22 голосов
/ 30 июля 2009

Цель

Я хочу динамически назначать обработчики событий некоторым элементам div на страницах сайта.

Мой метод

Я использую jQuery для привязки анонимных функций в качестве обработчиков для выбранных событий div.

Проблема

Код повторяет массив имен div и связанных URL. Имя div используется для установки цели привязки, то есть присоединяет этот обработчик события к этому событию div.

Хотя обработчики событий успешно связаны с каждым из событий div, действия, запускаемые этими обработчиками событий, только когда-либо предназначаются для последнего элемента в массиве.

Таким образом, идея заключается в том, что если пользователь наводит мышью на заданный элемент div, он должен запустить анимацию выдвижения для этого элемента div. Но вместо этого при наведении мыши на div1 (rangeTabAll) запускается анимация выдвижения для div4 (rangeTabThm). То же самое верно для div 2, 3 и т. Д. Порядок не имеет значения. Измените элементы массива вокруг, и события всегда будут нацелены на последний элемент в массиве, div4.

Мой код - (использует jQuery)

var curTab, curDiv;
var inlineRangeNavUrls=[['rangeTabAll','range_all.html'],['rangeTabRem','range_remedial.html'],
                ['rangeTabGym','range_gym.html'],['rangeTabThm','range_thermal.html']];
        for (var i=0;i<inlineRangeNavUrls.length;i++)
        {
            curTab=(inlineRangeNavUrls[i][0]).toString();
            curDiv='#' + curTab;
            if  ($(curDiv).length)
            {
                $(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
                $(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} );
            }
        }

Моя теория

Я либо не вижу ослепительно очевидную синтаксическую ошибку, либо проблему передачи по ссылке. Первоначально у меня был следующий оператор для установки значения curTab:

curTab=inlineRangeNavUrls[i][0];

Поэтому, когда возникла проблема, я понял, что при изменении (с помощью итерации цикла) ссылки на curTab я фактически изменил ссылку для всех предыдущих обработчиков событий анонимной функции на новое значение curTab а также .... именно поэтому обработчики событий всегда нацелены на последний div.

Так что мне действительно нужно было передать curTab value обработчикам событий анонимной функции, а не curTab object reference.

Итак, я подумал:

curTab=(inlineRangeNavUrls[i][0]).toString();

исправит проблему, но это не так. Та же сделка Очевидно, я упускаю некоторые ключевые и, возможно, очень базовые знания о проблеме Спасибо.

Ответы [ 4 ]

23 голосов
/ 30 июля 2009

Вам нужно создавать новую переменную при каждом прохождении цикла, чтобы она фиксировалась в замыканиях, которые вы создаете для обработчиков событий.

Однако простое перемещение объявления переменной в цикл не приведет к этому, потому что JavaScript не вводит новую область видимости для произвольных блоков .

Один из простых способов заставить внедрение новой области видимости - использовать другую анонимную функцию:

for (var i=0;i<inlineRangeNavUrls.length;i++)
{
  curDiv='#' + inlineRangeNavUrls[i][1];
  if ($(curDiv).length)
  {
    (function(curTab)
    {
      $(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
      $(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} );
    })(inlineRangeNavUrls[i][0]); // pass as argument to anonymous function - this will introduce a new scope
  }
}

Как предлагает Джейсон , вы можете действительно немного убрать это, используя встроенную в jQuery hover() функцию:

for (var i=0;i<inlineRangeNavUrls.length;i++)
{
  (function(curTab) // introduce a new scope
  {
  $('#' + inlineRangeNavUrls[i][1])
    .hover(
      function(){showHideRangeSlidingTabs(curTab, true);},
      function(){showHideRangeSlidingTabs(curTab, false);} 
    );
  // establish per-loop variable by passsing as argument to anonymous function
  })(inlineRangeNavUrls[i][0]); 
}
17 голосов
/ 30 июля 2009

что здесь происходит, так это то, что ваши анонимные функции образуют замыкание и уносят с собой свои внешние рамки. Это означает, что когда вы ссылаетесь на curTab внутри вашей анонимной функции, когда обработчик событий запускает эту функцию, он будет искать значение current curTab во внешней области. Это будет то, что вы в последний раз назначали curTab. (не то, что было назначено в то время, когда вы связали функцию)

вам нужно изменить это:

$(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );

к этому:

$(curDiv).bind("mouseover", 
    (function (mylocalvariable) { 
        return function(){
            showHideRangeSlidingTabs(mylocalvariable, true);
        } 
    })(curTab) 
);

это скопирует значение curTab в область видимости внешней функции, которую внутренняя функция возьмет с собой. Это копирование происходит одновременно с привязкой внутренней функции к обработчику событий, поэтому «mylocalvariable» отражает значение curTab в то время. Затем в следующий раз в цикле будет создана новая внешняя функция с новой областью действия, и в нее будет скопировано следующее значение curTab.

Ответ shog9 в основном выполняет то же самое, но его код немного более строг.

Это довольно сложно, но имеет смысл, если вы подумаете об этом. Закрытия странные.

edit: упс, забыл вернуть внутреннюю функцию. Исправлено.

1 голос
/ 27 марта 2012

Вы можете поместить значение вашей переменной в несуществующий тег, а позже вы можете прочитать их оттуда. Этот фрагмент является частью тела цикла:

 s = introduction.introductions[page * 6 + i][0]; //The variables content
 $('#intro_img_'+i).attr('tag' , s);              //Store them in a tag named tag
 $('#intro_img_'+i).click( function() {introduction.selectTemplate(this, $(this).attr('tag'));}  );  //retrieve the stored data
1 голос
/ 30 июля 2009

Я думаю, вы делаете это более сложным, чем нужно. Если все, что вы делаете, это назначаете скользящий эффект при наведении курсора мыши, попробуйте эффект hover с jquery.

$("#mytab").hover(function(){
    $(this).next("div").slideDown("fast");},
  function(){
    $(this).next("div").slideUp("fast");
});

Если вы разместите свой полный HTML, я точно скажу, как это сделать:)

...