Основываясь на том, как они построены, могут ли обратные вызовы также быть определены как замыкания? - PullRequest
4 голосов
/ 19 января 2009

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

function outerFunction(x, y) {
    function innerFunction() {
        return x + y + 10;
    }
    return innerFunction;
}

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

/* This is a contrived example, I know. 
 * Bear with me - it demonstrates the point I'm trying to convey. */

function submitHandler() {

    var oRequest = createRequest(); // assume I'm getting an instance of the xhr
    var sUsername = 'Tom';          // assume this is needed for work in the handler
    var This = this;
    oRequest.onreadystatechange = function() {
        This.handleResponse(oRequest, sUsername)
    }

}

function handleResponse(oResponse, sUsername) {
    if(oResponse.readyState === 4 && oResponse.status === 200) {
        // do work with the username
    } else {
        // we're not done yet...
    }
}

Я понимаю, что функция handleResponse также может быть написана как анонимная функция в контексте submitHandler, но я считаю, что более сложный код Ajax может быть более читабельным и легко поддерживаемым, если обратные вызовы определены вне области функции, обращающейся к ним. Опять же, это надуманный пример, который я использую в надежде просто продемонстрировать смысл моего вопроса.

Ответы [ 3 ]

3 голосов
/ 19 января 2009

Я полностью согласен с тем, что слишком много встроенных функций закрытия не улучшают читабельность. С другой стороны, мне категорически не нравится стиль написания замыканий var self = this, для которого this является лишь вариантом, поскольку он все еще слишком многословен в своем объявлении, и вы вводите свое собственное новое ключевое слово, будь то this или self.

То, что вы хотите, это карри / связать метод.

function $A(o)
{
    var a = [];
    for (var i = 0; i < o.length; ++i)
        a.push(o[i]);
    return a;
}

Function.prototype.bind = function()
{
   var _method = this;
   var a = $A(arguments)
   var o = a.shift();

   return function()
   {
       return _method.apply(o, a.concat($A(arguments)));
   }
}

Function.prototype.curry = function()
{
   var _method = this;
   var a = $A(arguments);

   return function()
   {
       return _method.apply(null, a.concat($A(arguments)));
   }
}

Методы взяты из библиотеки Prototype . Я использую их даже в проектах, которые не используют библиотеку Prototype, поскольку они очень полезны!

В вашем случае это означает вместо записи:

var This = this;
oRequest.onreadystatechange = function() {
    This.handleResponse(oRequest, sUsername)
}

Теперь вы можете написать:

oRequest.onreadystatechange = this.handleResponse.curry(oRequest,sUsername);

Однако, если вы хотите передать значение этого ключевого слова, вы можете сделать это

oRequest.onreadystatechange = this.handleResponse.bind(this,oRequest,sUsername);

handleResponse при вызове будет иметь тот же this контекст, что и submitHandler.

3 голосов
/ 19 января 2009

Да, вы правы, полагая, что это закрытие по определению.

Звучит так, будто вы знаете свои вещи, но вот хорошая, обширная статья о javascript замыканиях .

1 голос
/ 19 января 2009

Ваш первый пример не является закрытием. Было бы, если бы вы вернули innerFunction, но вы выполняете innerFunction и возвращаете результат.

Второй пример работает и использует замыкание. Одно из предостережений, связанных с этой конкретной техникой, заключается в том, что в некоторых реализациях браузера вы можете столкнуться с довольно серьезной утечкой памяти (конечно, IE может быть и другим). Вы создадите циклическую ссылку между xhr, хранящимся в oRequest, onreadystatechange, ссылающимся на функцию, и областью действия функции является область, в которой хранится xhr. Мало того, что xhr останется в памяти, так и ответ, который, если он большой (особенно если XML), может быстро поглотить память.

Установите свойство onreadystatechange для пустой функции глобального уровня во время обработки ответа. Это разорвет круг.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...