【问题标题】:Scoping problem with Javascript callbackJavascript回调的范围问题
【发布时间】:2010-06-09 12:59:05
【问题描述】:

我在让回调函数工作时遇到了一些问题。这是我的代码:

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(又名窗口对象),因为我的回调函数被调用为函数而不是成员方法。我的理解是我需要使用闭包来保持“这个”,但是,我无法让它工作。

如果有人能告诉我我应该做什么,我将不胜感激。我仍在思考闭包是如何工作的,特别是在这种情况下它们将如何工作。

谢谢!

【问题讨论】:

    标签: javascript callback closures this


    【解决方案1】:

    最直接的做法就是将“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”的对象。

    【讨论】:

    • 确实,这里发生的情况是obj 被锁定在评估function() { obj.readSuccess(); } 时创建的闭包中。除非您使用像 Prototype 或 jQuery 这样的库,否则没有其他方法可以做到这一点;它们有一个.bind() 函数,可以透明地创建闭包。
    • 不要挑剔,但为了 StackOverflow 的后代,jQuery 版本被称为“代理”,而不是“绑定”。
    • 非常有意义。 jQuery 代理功能有效,非常棒。然而,为了完整起见,如果我不使用 jQuery,我仍然有点困惑如何处理这个问题。在我的示例中,readSuccess 有 1 个参数,respnse,它会在以后填充。所以我说 function() { obj.readSuccess(wah??); 没有意义。 } 自从哇??应该会在稍后的时间通过。我试过 function() { obj.readSuccess; } 没有运气。有什么想法吗?
    • 你是完全正确的,nazbot。我们不应该假设 readSuccess 是在没有参数的情况下调用的,因为可能不是这样。请参阅我的答案以获得解决方案。
    【解决方案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 分配给某个变量并将其作为 this 的方法调用。这可以通过 Pointy 演示的闭包来完成。由于我不知道“SomeAjaxCall”实际上做了什么,所以很难知道this 的值是否真的丢失了,以及是否真的需要var obj = this。很可能不是,所以你可以使用这种代码:

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

    在您的情况下,这将是(edit: 添加传递给处理程序的参数):

    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)
      );
    }
    

    【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-22
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 2016-04-24
      • 2011-04-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多