【问题标题】:JavaScript Callback Scope [duplicate]JavaScript回调范围[重复]
【发布时间】:2010-09-16 00:59:59
【问题描述】:

我在使用普通的旧 JavaScript(无框架)在回调函数中引用我的对象时遇到了一些问题。

function foo(id) {
    this.dom = document.getElementById(id);
    this.bar = 5;
    var self = this;
    this.dom.addEventListener("click", self.onclick, false);
}

foo.prototype = {
    onclick : function() {
        this.bar = 7;
    }
};

现在当我创建一个新对象时(在 DOM 加载后,使用 span#test)

var x = new foo('test');

onclick 函数中的 'this' 指向 span#test 而不是 foo 对象。

如何在 onclick 函数中获取对我的 foo 对象的引用?

【问题讨论】:

  • 保存参考this.cb = this.onclick.bind(this); addEventListener("click", this.cb);

标签: javascript events binding scope callback


【解决方案1】:

(提取了一些在其他答案中隐藏在 cmets 中的解释)

问题出在下面一行:

this.dom.addEventListener("click", self.onclick, false);

在这里,您传递一个函数对象以用作回调。当事件触发时,该函数被调用,但现在它与任何对象(this)都没有关联。

可以通过将函数(及其对象引用)包装在闭包中来解决问题,如下所示:

this.dom.addEventListener(
  "click",
  function(event) {self.onclick(event)},
  false);

由于变量 self 在创建闭包时被赋值 this,所以闭包函数会在以后调用时记住 self 变量的值。

解决这个问题的另一种方法是制作一个实用函数(并避免使用变量来绑定this):

function bind(scope, fn) {
    return function () {
        fn.apply(scope, arguments);
    };
}

更新后的代码如下所示:

this.dom.addEventListener("click", bind(this, this.onclick), false);

Function.prototype.bind 是 ECMAScript 5 的一部分,提供相同的功能。所以你可以这样做:

this.dom.addEventListener("click", this.onclick.bind(this), false);

对于尚不支持 ES5 的浏览器,MDN provides the following shim:

if (!Function.prototype.bind) {  
  Function.prototype.bind = function (oThis) {  
    if (typeof this !== "function") {  
      // closest thing possible to the ECMAScript 5 internal IsCallable function  
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");  
    }  

    var aArgs = Array.prototype.slice.call(arguments, 1),   
        fToBind = this,   
        fNOP = function () {},  
        fBound = function () {  
          return fToBind.apply(this instanceof fNOP  
                                 ? this  
                                 : oThis || window,  
                               aArgs.concat(Array.prototype.slice.call(arguments)));  
        };  

    fNOP.prototype = this.prototype;  
    fBound.prototype = new fNOP();  

    return fBound;  
  };  
} 

【讨论】:

  • closure() 可能是一个更好的名字。
  • 当你使用多个对象时,闭包不起作用;您将始终引用创建的最后一个对象。实用函数有效,因为参数每次都是新存储的。
【解决方案2】:
this.dom.addEventListener("click", function(event) {
    self.onclick(event)
}, false);

【讨论】:

  • 谢谢,但你能解释一下为什么我需要在那里创建一个匿名函数吗?为什么这会改变绑定?
  • 每当触发事件时,'this' 指的是调用该事件的对象。
  • 当您在问题中使用表单时,self.onclick 只是一个匿名函数,在处理时,就像您将其直接附加到跨度一样。当你将它包装在一个闭包中时, self.onclick(event) 真的会做你想要的,例如使用定义闭包的上下文中的“self”。
  • 这仅适用于正在创建的一个对象;考虑function () {var i; for (i=0; i<5; i++) { setTimeout( function() { console.log(i); }, 1000); } }; f();的输出
  • 我收回这一点 - 每次调用都会创建一个新的 var self ,并且与我之前评论中的示例不同,它不会改变。 (也有错别字,应该以function f()开头)
【解决方案3】:

jQuery 用户正在寻找此问题的解决方案,您应该使用jQuery.proxy

【讨论】:

    【解决方案4】:

    解释是self.onclick 并不意味着你认为它在 JavaScript 中的含义。它实际上表示对象self 原型中的onclick 函数(不以任何方式引用self 本身)。

    JavaScript 仅有函数,没有像 C# 那样的委托,因此不可能将方法和对象作为回调传递给它。

    在回调中调用方法的唯一方法是在回调函数中自己调用它。因为 JavaScript 函数是闭包,所以它们能够访问在创建它们的作用域中声明的变量。

    var obj = ...;
    function callback(){ return obj.method() };
    something.bind(callback);
    

    【讨论】:

    • 我将原型函数称为“onclick”以轻松关联它。我知道它不会被实际的 DOM onclick 事件自动调用,这就是为什么我试图让事件侦听器将我的对象的 onclick 与 DOM 的 onclick 函数绑定。
    • 这不是我的意思,我了解您的 onclick 功能。我的观点是 self.onclick 和 foo.prototype.onclick 之间的 JavaScript 没有区别。 JavaScript 中没有办法说“这个方法绑定到这个对象”。
    • 唯一的办法就是使用闭包。
    【解决方案5】:

    available here 是对问题的一个很好的解释(到目前为止,我在理解解决方案时遇到了问题)。

    【讨论】:

      【解决方案6】:

      我写了这个插件...

      我觉得很有用

      jquery.callback

      【讨论】:

        【解决方案7】:

        this 是 JS 最令人困惑的地方之一:'this' 变量意味着最局部的对象......但函数也是对象,所以 'this' 指向那里。还有其他一些微妙的地方,但我不记得全部了。

        我通常避免使用“this”,只定义一个本地“me”变量并使用它。

        【讨论】:

        • 您的陈述完全不准确。函数的 apply() 和 call() 方法都允许您执行一个函数,其中 'this' 引用您在调用中传递的变量。
        • @Kenaniah:.... 因此更加混乱,代码的确切含​​义取决于它的调用方式!
        • JavaScript 中的函数非常灵活,不使用this 很难解决问题。 this 是 JS 中唯一的动态作用域功能,它为语言增加了很多功能。是的,函数是 JS 中的一等对象这一事实暗示了“执行上下文指针”的动态特性。如果使用得当,这是一个很棒的功能。
        猜你喜欢
        • 1970-01-01
        • 2015-06-14
        • 1970-01-01
        • 1970-01-01
        • 2014-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-31
        相关资源
        最近更新 更多