Каковы варианты использования для закрытий/функций обратного вызова в JavaScript?

Я слушал разговор Crockford о закрытиях JavaScript и убежден в преимуществе сокрытия информации, но у меня нет твердого понимания того, когда использовать функции обратного вызова.

Это - главным образом истинный оператор, что человек мог выполнить ту же функциональность с или без обратных вызовов.

Как кто-то, кто написание кода, какую эвристику или сигналы я должен иметь в виду при определении, когда использовать обратные вызовы/закрытия?

Я не ищу общий оператор 'Closures make more secure code', скорее список практических примеров или эмпирических правил для того, когда обратные вызовы являются верной мыслью.

Презентация Crockford: http://www.yuiblog.com/blog/2010/04/08/video-crockonjs-5/

21
задан Kevin Brown 20 February 2015 в 02:11
поделиться

3 ответа

Во-первых:

  • Обратный вызов: функция, переданная в качестве аргумента другой функции, обычно вызываемая в результате возникновения события.
  • Закрытие: оставшаяся сфера действия. Т.е. концепция, согласно которой, когда вы объявляете функцию внутри другой функции, область видимости внешней функции становится доступной внутри функции внутренней .

Обратные вызовы также могут быть закрытием, но не всегда.

Это обратный вызов:

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);
}
33
ответ дан 29 November 2019 в 06:56
поделиться

Эта запись от Mozilla может дать ответ, почему и когда используются замыкания

Кроме того, см. Этот набор примеров (особенно раздел «Что можно сделать с замыканиями?», В котором есть следующие примеры) : :

  • Пример 1: setTimeout со ссылками на функции
  • Пример 2: Связывание функций с методами экземпляра объекта
  • Пример 3: Инкапсуляция связанных функций

Я чувствую, что это можно проследить до Крокфорда, но классическое использование замыкания предназначены для эмуляции частного экземпляра или статических переменных (которых нет в JavaScipt).

5
ответ дан 29 November 2019 в 06:56
поделиться

Допустим, вам нужна функция, которая возвращает уникальное значение "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'),
  // ...
};

Теперь я могу вызвать getId.div(), чтобы получить новое значение "id" для нового

. Эта функция была создана путем вызова функции, которая предоставляет два значения, спрятанные в закрытии: префиксную строку (переданную в качестве аргумента) и счетчик (var, объявленный в области видимости закрытия).

Как только вы привыкнете к нему, он станет настолько гибким и полезным, что вам будет больно возвращаться в среду без него.

О, и вот совет, который поможет вам не попасть на StackOverflow, если вы попробуете это: это вопрос, который всплывает постоянно:

for (var i = 0; i < 10; ++i) {
  var id = "foo" + i;
  var element = document.getElementById(id);
  element.onclick = function() {
    alert("hello from element " + i);
  };
}

В чем здесь проблема? Ну, переменная "i", на которую ссылается эта функция, является "i" из области видимости, в которой выполняется этот цикл. Эта переменная, как вы заметили, увеличивается в цикле (да, верно?). Так вот, каждая из этих маленьких функций, созданных и назначенных в качестве обработчиков событий, будет разделять ту же самую, единственную переменную "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);
}

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

Подведем итог: техника использования замыканий встречается постоянно, как только вы привыкнете к ней. Это не бесплатный билет в новую страну безошибочного программирования, не поймите меня неправильно. Однако это очень полезная и гибкая парадигма.

14
ответ дан 29 November 2019 в 06:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: