Javascript回调的范围问题

时间:2010-06-09 12:59:06

标签: javascript callback closures this

我在使用回调函数时遇到了一些麻烦。这是我的代码:

SomeObject.prototype.refreshData = function()
{
  var read_obj = new SomeAjaxCall("read_some_data", { }, this.readSuccess, this.readFail);
}

SomeObject.prototype.readSuccess = function(response)
{
    this.data = response;
    this.someList = [];
    for (var i = 0; i < this.data.length; i++)
    {
      var systemData = this.data[i];
      var system = new SomeSystem(systemData);
      this.someList.push(system);
    }
    this.refreshList();
}

基本上SomeAjaxCall正在对数据进行ajax请求。如果它工作,我们使用回调'this.readSuccess',如果它失败'this.readFail'。

我已经发现SomeObject.readSuccess中的'this'是全局的(也就是窗口对象),因为我的回调被称为函数而不是成员方法。我的理解是我需要使用闭包来保持'this',但是,我无法让它工作。

如果有人能告诉我我应该做什么,我会非常感激。我仍然围绕着闭包的工作方式,特别是它们如何在这种情况下工作。

谢谢!

3 个答案:

答案 0 :(得分:2)

最直接的做法是将“this.readSuccess”包装在另一个函数中:

SomeObject.prototype.refreshData = function()
{
  var obj = this;
  var read_obj = new SomeAjaxCall("read_some_data", { }, 
    function() { obj.readSuccess(); }, function() { obj.readFail(); });
}

一些Javascript框架提供了一个实用程序来将一个函数“绑定”到一个对象,这只是意味着它为你创建了一个小函数。请注意,变量“obj”将由那些小函数“记住”,因此当您的处理程序被调用时,“this”引用将是用于调用“refreshData”的对象。

答案 1 :(得分:2)

这里的问题不是完全关闭或作用域问题。问题是,当您将this.readSuccess分配给变量时,您可以自行分配函数,而不需要它原始所属的对象的任何概念。

以同样的方式,您可以使用常规的“独立”功能并将其用作对象的方法:

function hello() {
    alert("Hello "+this.planet);
}
var planet = "Earth";
hello(); // -> 'Hello Earth'

var Venus = {
    planet: "Venus"
};
hello.apply(Venus); // -> 'Hello Venus'
Venus.hello = hello;
Venus.hello(); // -> 'Hello Venus'

您可以在此示例中复制您的问题

var helloVenus = Venus.hello;
helloVenus(); // -> 'Hello Earth'

所以你的问题是将this.readSuccess分配给某个变量 并将其作为此 的方法调用。如Pointy所证明的那样可以通过闭合来完成。由于我不知道“SomeAjaxCall”实际上做了什么,因此很难知道this的值是否实际丢失以及实际是否需要var obj = this。有可能它不是,所以你可以使用这种代码:

var helloVenus = function() { Venus.hello() }
helloVenus(); // -> 'Hello Venus'

在你的情况下,那将是(编辑:添加传递给处理程序的参数):

SomeObject.prototype.refreshData = function()
{
  var read_obj = new SomeAjaxCall(
    "read_some_data",
    { },
    function () { this.readSuccess.apply(this, arguments) },
    function () { this.readFail.apply(this, arguments) }
  );
}

如前所述,几个js框架提供bind函数来简化此类问题。但是你不需要一个完整的框架:这是一个非常好的Function#bind方法,可以使用普通的javascript:

Function.prototype.bind = function(obj) {
    var __method = this;
    var args = [];
    for(var i=1; i<arguments.length; i++)
        args.push(arguments[i]);
    return function() {
        var args2 = [];
        for(var i=0; i<arguments.length; i++)
            args2.push(arguments[i]);
        return __method.apply(obj, args.concat(args2));
    };
}

Function#bind的帮助下,您可以写下:

SomeObject.prototype.refreshData = function()
{
  var read_obj = new SomeAjaxCall(
    "read_some_data",
    { },
    this.readSuccess.bind(this),
    this.readFail.bind(this)
  );
}

答案 2 :(得分:0)

以下网站似乎表明问题可能在您的“班级”建筑中比在使用中更多。如果你读到“使用原型属性重写”,他们会写出“类”结构的特定方法将使方法保持全局而不是基于实例。也许是另一种创作方法?

http://devedge-temp.mozilla.org/viewsource/2001/oop-javascript/