根据它们的构造方式,回调也可以定义为闭包吗?

时间:2009-01-19 13:16:03

标签: javascript closures callback

在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 个答案:

答案 0 :(得分:3)

是的,根据定义你假设它是一个闭包是正确的。

听起来你知道你的东西,但here is a good, extensive article on javascript closures

答案 1 :(得分:3)

我完全同意太多的内联闭包函数不会增强可读性。另一方面,我强烈不喜欢编写闭包的var self = this样式,this只是一个变体,因为它在声明中仍然过于冗长,并且你引入了自己的新'关键字',无论是是thisself

你想要的是咖喱/绑定方法。

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上下文。

答案 2 :(得分:1)

你的第一个例子不是封闭。如果您返回了innerFunction,但是您正在执行innerFunction并返回结果。

第二个例子有效并正在使用闭包。使用这种特定技术的一个警告是,在某些浏览器实现中可能会导致非常严重的内存泄漏(IE当然也可能是其他浏览器)。您将在oRequest中保存的xhr,引用函数的onreadystatechange和作为xhr所在范围的函数作用域之间创建循环引用。 xhr不仅会保留在内存中,因此响应也会很大(特别是如果是XML)可以快速吞噬内存。

在处理响应期间将onreadystatechange属性设置为空的全局级别函数。这将打破这个圈子。