JavaScript 回调范围

IT技术 javascript events binding scope callback
2021-02-02 18:03:03

我在使用普通的旧 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 对象的引用?

6个回答

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

问题在于以下行:

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 提供了以下 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;  
  };  
} 
当您使用多个对象时,闭包将不起作用;您将始终指的是最后一个创建的对象。效用函数之所以起作用,是因为参数每次都是新存储的。
2021-03-16 18:03:03
不过,closure() 可能是一个更好的名字。
2021-04-09 18:03:03
this.dom.addEventListener("click", function(event) {
    self.onclick(event)
}, false);
当您在问题中使用表单时,self.onclick 只是一个匿名函数,处理时就像将它直接附加到跨度一样。当您将它包装在一个闭包中时,self.onclick(event) 确实可以满足您的需求,例如使用定义闭包的上下文中的 'self'。
2021-03-26 18:03:03
谢谢,但你能解释一下为什么我需要在那里创建一个匿名函数吗?为什么这会改变绑定?
2021-04-01 18:03:03
我收回这一点 -var self每次调用都会创建一个新的,并且与我之前评论中的示例不同,它没有改变。(里面也有错别字,应该以 开头function f()
2021-04-01 18:03:03
这仅适用于正在创建的一个对象;考虑输出function () {var i; for (i=0; i<5; i++) { setTimeout( function() { console.log(i); }, 1000); } }; f();
2021-04-10 18:03:03
每当触发事件时,“this”指的是调用该事件的对象。
2021-04-12 18:03:03

对于寻找此问题解决方案jQuery用户,您应该使用jQuery.proxy

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

JavaScript 只有函数,没有像 C# 那样的委托,所以不可能传递一个方法和它应该作为回调应用到的对象。

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

var obj = ...;
function callback(){ return obj.method() };
something.bind(callback);
那不是我的观点,我理解您的 onclick 功能。我的观点是,self.onclick 和 foo.prototype.onclick 在 JavaScript 中没有区别。在 JavaScript 中没有办法说“这个方法绑定到这个对象”。
2021-03-19 18:03:03
唯一的方法是使用闭包。
2021-03-20 18:03:03
我调用原型函数“onclick”来轻松关联它。我知道它不会被实际的 DOM onclick 事件自动调用,这就是为什么我试图做事件侦听器来将我的对象的 onclick 与 DOM 的 onclick 函数绑定。
2021-03-22 18:03:03

问题的一个很好的解释(我在理解到目前为止描述的解决方案时遇到了问题)可以在这里找到