对何时在事件处理程序中使用`bind`感到困惑

时间:2017-05-07 08:09:14

标签: javascript this

以下成功打印'foo'。

var obj = {
    name: 'foo',
    printName: function printName() {
      console.log(this.name);
    }
  };

var printButton= document.getElementById('printIt');

printButton.addEventListener('click', function(){
  obj.printName(); 
});

但以下情况并非如此:

printButton.addEventListener('click', obj.printName() );

我知道解决方案......只需使用bind,以便我们引用obj对象。即:

printButton.addEventListener('click', obj.printName.bind(obj) );

为什么我们不需要在第一个示例中使用bind。我不知道为什么在匿名函数中包含obj.printName()函数调用会导致console.log正确引用并正确打印this,但在click之后直接调用时,您需要使用bind

2 个答案:

答案 0 :(得分:1)

好吧,我在这个问题上评论了一些很好的信息,所以我不妨回答一下!

函数是第一类

好的,让我们从javascript的一些基本原理开始,这与其他一些编程语言非常不同:javascript函数是一等公民 - 这只是一种奇特的说法您可以将函数保存到变量中,也可以将函数传递给其他函数。

const myFunction = function () { return 'whoa a function'; }
array.map(function () { return x + 1; });

由于这个奇妙的功能,表达式之间存在很大差异:

表达式1

obj.printName

表达式2

obj.printName();

在表达式1中:函数未被调用,因此表达式的值为function

在表达式2中:正在调用函数,因此表达式的值是函数返回的值。就你而言,那是undefined

addEventListener

方法addEventListener有两个参数:

  1. 事件类型的string
  2. 将在事件触发时运行的function
  3. 下车,那是什么意思?

    致电时

    // doesn't work
    printButton.addEventListener('click', obj.printName() );
    

    您没有将function类型的值传递给addEventListener方法,而是实际传递undefined

    // works
    printButton.addEventListener('click', obj.printName.bind(obj) );
    
    然后

    起作用(出于一个原因),因为第二个参数实际上是function类型。

    bind做什么?为什么它会返回一个函数?

    现在我们需要讨论bind实际做了什么。它与指针* this相关。

    * by pointer,我指的是某个对象的引用标识符

    bind是一个存在于每个函数对象上的方法,它只是将所需对象的this指针绑定到函数

    最好通过一个例子来说明:

    假设您有一个类Fruit,其方法为printName。现在我们知道您可以将方法保存到变量中,让我们尝试一下。在下面的示例中,我们分配了两件事:

      使用boundMethod
    1. bind
    2. unboundMethod没有使用bind
    3. 
      
      class Fruit {
        constructor() {
          this.name = 'apple';
        }
        
        printName() {
          console.log(this.name);
        }
      }
      
      const myFruit = new Fruit();
      
      // take the method `printName`
      const boundMethod = myFruit.printName.bind(myFruit);
      const unboundMethod = myFruit.printName;
      
      boundMethod(); // works
      unboundMethod(); // doesn't work
      
      
      

      那么当你不打电话给bind时会发生什么?为什么这不起作用?

      如果您在这种情况下不调用bind,则可以将存储到标识符unboundMethod中的函数的值视为:

      // doens't work
      const unboundMethod = function() {
          console.log(this.name);
      }
      

      其中函数的内容与printName类中的方法Fruit的内容相同。你知道为什么这是一个问题吗?

      因为this指针仍在那里,但它想要引用的对象不再在范围内。当你尝试调用unboundMethod时,你&# 39;我会收到错误,因为它无法在name中找到this

      那么当您使用bind时会发生什么?

      松散 bind可以被视为将this函数的值替换为您传入bind的对象。

      因此,如果我将myFruit.printName.bind(myFruit)分配给boundMethod,那么您可以将此类作业视为:

      // works
      const boundMethod = function() {
          console.log(myFruit.name);
      }
      

      其中this替换为myFruit

      底线/ TL; DR

        

      何时在事件处理程序中使用bind

      如果要将函数内的this es替换为另一个对象/指针,则需要使用Function.prototype.bind如果您的功能没有使用this,那么您就不需要使用bind

        

      为什么我们不需要在第一个例子中使用bind?

      如果您不需要采取方法" (即取function类型的值),那么你就不需要使用bind 另一种说法是:如果你直接从对象,你不需要bind同一个对象。

      在包装函数中,您直接调用对象的方法(如表达式2)。因为您在没有"采取方法的情况下调用该方法" (我们"将方法转换为Fruit示例中的变量),您不需要使用bind

      printButton.addEventListener('click', function(){
        // directly invoke the function
        // no method "taking" here
        obj.printName();
      });
      

      希望这会有所帮助:D

答案 1 :(得分:0)

注意:您需要在2中调用printButton.addEventListener('click', obj.printName() );不带括号,因为您要传递该功能。

答案在于obj.printName()在Javascript中绑定的方式。在JS中,调用函数的方式决定了this的绑定方式。所以当你提供如下的回调函数时:

this

注意,printName是通过printButton.addEventListener('click', function(){ obj.printName(); }); 表示法调用的。当dot绑定到点之前的对象时,这称为隐式绑定规则,在本例中为this。显然,在这种情况下,您可以获得预期的输出。

但是,当你这样称呼它时:

obj

请注意,您传递的所有内容都是obj内部函数的printButton.addEventListener('click', obj.printName ); 。所以在这种情况下,关于obj的信息丢失了。换句话说,回调函数的代码没有关于可能用于设置address的obj的信息。它只是要调用的函数的地址。

希望这有帮助!

修改: 看看我称之为模仿本机绑定的this的{​​{1}}实现。这只是为了说明native crude函数如何返回一个新函数。

bind2

注意:函数f()在这里是bind的方式。 f()与Function.prototype.bind2 = function (context) { var callBackFunction = this;//Store the function to call later return function () { //return a new function callBackFunction.call(context);//Later when called, apply //context, this is `obj` passed //in bind2() } }; function hello() { alert(this.name); } obj = { name:'ABC' }; var f = hello.bind2(obj); f(); 具有硬绑定hard bound。您现在无法将this更改为obj以外的其他人。这是绑定的另一件事,可能会帮助你了解。