Я слушал разговор Crockford о закрытиях JavaScript и убежден в преимуществе сокрытия информации, но у меня нет твердого понимания того, когда использовать функции обратного вызова.
Это - главным образом истинный оператор, что человек мог выполнить ту же функциональность с или без обратных вызовов.
Как кто-то, кто написание кода, какую эвристику или сигналы я должен иметь в виду при определении, когда использовать обратные вызовы/закрытия?
Я не ищу общий оператор 'Closures make more secure code', скорее список практических примеров или эмпирических правил для того, когда обратные вызовы являются верной мыслью.
Презентация Crockford: http://www.yuiblog.com/blog/2010/04/08/video-crockonjs-5/
Во-первых:
Обратные вызовы также могут быть закрытием, но не всегда.
Это обратный вызов:
someProcess(myCallback);
function myCallback() {
alert('Done...');
}
function someProcess(callback) {
// does stuff...
// ...
callback();
}
Замыкание:
function foo(msg) {
function bar() {
// I can access foo's scope
// (i.e. bar can access everything that foo can access)
alert(msg);
}
return bar;
}
foo('hello')(); // alerts "hello"
Одним из распространенных способов использования замыканий является обеспечение сокрытия информации, что помогает обеспечить некую инкапсуляцию в язык. Взгляните на шаблон модуля , чтобы увидеть это в действии.
Другое распространенное использование - привязка обработчиков событий к элементам. Например.
var myElements = [ /* DOM Collection */ ];
for (var i = 0; i < 100; ++i) {
myElements[i].onclick = function() {
alert( 'You clicked on: ' + i );
};
}
Это не сработает. К моменту щелчка по элементу переменная i
равна 99
. Чтобы это работало правильно, мы могли бы использовать замыкание для захвата значения i
:
function getHandler(n) {
return function() {
alert( 'You clicked on: ' + n );
};
}
for (var i = 0; i < 100; ++i) {
myElements[i].onclick = getHandler(i);
}
Эта запись от Mozilla может дать ответ, почему и когда используются замыкания
Кроме того, см. Этот набор примеров (особенно раздел «Что можно сделать с замыканиями?», В котором есть следующие примеры) : :
Я чувствую, что это можно проследить до Крокфорда, но классическое использование замыкания предназначены для эмуляции частного экземпляра или статических переменных (которых нет в JavaScipt).
Допустим, вам нужна функция, которая возвращает уникальное значение "id", используемое при создании новых элементов DOM. В Java вы можете создать класс с внутренним приватным счетчиком, а затем метод, который добавляет счетчик к некоторой префиксной строке. В Javascript:
var getId = (function() {
var counter = 0;
return function() {
return "prefix" + counter++;
};
})();
Теперь переменная "getId" связана с функцией, которая создана другой функцией, и создана таким образом, что у нее есть постоянная переменная для использования между вызовами. Аналогично, если бы я хотел иметь семейство функций "getId" (скажем, по одной для каждого типа элемента DOM, который я мог бы добавить), я мог бы сделать следующее:
var getIdFunc = function(prefix) {
var counter = 0;
return function() {
return prefix + counter++;
};
};
var getId = {
'div': getIdFunc('div'),
'span': getIdFunc('span'),
'dl': getIdFunc('dl'),
// ...
};
Теперь я могу вызвать Как только вы привыкнете к нему, он станет настолько гибким и полезным, что вам будет больно возвращаться в среду без него. О, и вот совет, который поможет вам не попасть на StackOverflow, если вы попробуете это: это вопрос, который всплывает постоянно: В чем здесь проблема? Ну, переменная "i", на которую ссылается эта функция, является "i" из области видимости, в которой выполняется этот цикл. Эта переменная, как вы заметили, увеличивается в цикле (да, верно?). Так вот, каждая из этих маленьких функций, созданных и назначенных в качестве обработчиков событий, будет разделять ту же самую, единственную переменную "i" в области видимости закрытия. Упс! Решение заключается в следующем: Мы делаем копию внешней "i" в собственную область видимости закрытия, так что теперь у каждого обработчика событий есть своя собственная! Подведем итог: техника использования замыканий встречается постоянно, как только вы привыкнете к ней. Это не бесплатный билет в новую страну безошибочного программирования, не поймите меня неправильно. Однако это очень полезная и гибкая парадигма. getId.div()
, чтобы получить новое значение "id" для нового var
, объявленный в области видимости закрытия).
for (var i = 0; i < 10; ++i) {
var id = "foo" + i;
var element = document.getElementById(id);
element.onclick = function() {
alert("hello from element " + i);
};
}
for (var i = 0; i < 10; ++i) {
var id = "foo" + i;
var element = document.getElementById(id);
element.onclick = (function(iCopy) {
return function() {
alert("hello from element " + iCopy);
};
})(i);
}