Использование замыканий с функцией dojo.connect

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

Однократные соединения
Рассмотрим ситуацию, когда необходимо установить соединение, которое должно быть разорвано после первого же срабатывания. Следующий пример показывает, как выполнить эту работу с минимальными усилиями:
var handle = dojo.connect(
dojo.byId("foo"), //некоторый элемент div
"onmouseover",
function(evt) {
//здесь находится тело некоторого обработчика...
dojo.disconnect(handle);
}
);

Если вы еще недостаточно уверенно чувствуете себя при работе с замыканиями, вашей первой реакцией может быть: «То, что мы только что сделали, попросту невозможно». В конце концов, переменная handle получает значение, возвращаемое функцией dojo.connect, и при этом ссылка на нее используется внутри функции, которая передается в dojo.connect в качестве параметра. Чтобы лучше понять ситуацию, разберем все происходящее в деталях:
1. Вызывается функция dojo.connect, и, хотя одним из ее параметров является анонимная функция, она в этот момент еще не выполняется.
2.


Любые переменные внутри анонимной функции (такие как handle) связаны с ее областью видимости, и хотя они присутствуют в телефункции, фактическое обращение к ним происходит, только когда функция действительно будет вызвана, поэтому в этом программном коде не возникает никакой ошибки.
3. Функция dojo.connect возвращает значение переменной handle еще до того, как анонимная функция будет вызвана. Поэтому к моменту вызова анонимной функции значение переменной будет определено и может передаваться функции dojo.disconnect.

Установка соединений в цикле
Еще одна ситуация, часто встречающаяся на практике, – необходимость устанавливать соединения в теле цикла. Предположим, что у нас в странице имеется несколько элементов – foo0, foo1, ..., foo9, и нам необходимо при перемещении указателя мыши над этими элементами выводить уникальное для каждого из них число. При первой попытке вы могли бы прийти к следующему фрагменту программного кода, который, впрочем, не даст ожидаемого результата:
/* Следующий фрагмент работает не так, как ожидается! */
for (var i=0; i < 10; i++) {
var foo = dojo.byId("foo"+i);
var handle = dojo.connect(foo, "onmouseover", function(evt) {
console.log(i);
dojo.disconnect(handle);
});
}

Если вы запустите этот фрагмент в Firebug на странице с серией указанных элементов, вы быстро обнаружите, что тут имеется проблема.


А именно, в консоли всегда будет выводиться число 10, то есть всеми функциямиобработчиками будет использоваться последнее значение переменной i, а это означает, что все десять обработчиков по ошибке будут пытаться разорвать одно и то же соединение. Остановимся на минутку, чтобы обдумать ситуацию. Однако такому неожиданному для вас поведению имеется разумное объяснение: внутри замыкания, образованного анонимной функцией, переданной функции dojo.connect, не выполняется разрешение имени i, пока функция действительно не будет вызвана, но к этому моменту переменная i будет иметь последнее полученное в цикле значение.

Следующие изменения устраняют проблему, захватывая значение переменной i в ловушку цепочки областей видимости, чтобы при после дующем обращении к переменной возвращалось значение, которое было на момент вызова функции dojo.connect:
for (var i=0; i < 10; i++) {
(function() {
var foo = dojo.byId("foo"+i);
var current_i = i; //поймать в ловушку замыкания
var handle = dojo.connect(foo, "onmouseover",
function(evt) {
console.log(current_i);
dojo.disconnect(handle);
}
);
})(); //выполнить анонимную функцию немедленно
}

Поначалу этот фрагмент программного кода может показаться замысловатым, но на самом деле здесь нет ничего сложного.


Все тело цикла оформлено в виде анонимной функции, которая тут же и выполняется, а поскольку анонимные функции образуют замыкание для всего, что находится внутри, значение переменной i попадает в «ловушку» переменной current_i, которая может быть разрешена во время выполнения обработчика события. Точно так же правильно будет разрешаться и переменная handle, потому что она тоже существует в пределах замыкания, образованного встроенной анонимной функцией.

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

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

Соединения в тексте разметки
Стоит заметить, что точно так же можно создавать соединения для дид житов вообще без (или с минимальным использованием) программного кода JavaScript при использовании специальных тегов SCRIPT с функцией dojo.connect в тексте разметки.

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Dojo на эту тему:
Нормализация событий и клавиатуры
Обработчики событий
Организация взаимодействий по подписке
Распространение событий