函数调用绑定的区别?

时间:2016-10-18 11:48:02

标签: javascript binding prototype

我有一个对象"我"为其定义的功能。

me.prototype.f = function(args){
    console.log("f called");
};

我需要在异步函数中提供f作为回调。

me.prototype.operation = function (){
    var self = this;
    var client; // an io client
    //client.on("data", self.f);                --a
    //client.on("data", self.f.bind(self))      --b
}

和&之间有什么区别吗? b在这种情况下,是否存在a)可能失败的情况?

在哪些情况下,我们会在没有绑定的情况下遇到问题?通常情况下,值可能会根据上下文而改变。 (例如,使用循环索引值进行闭包)
那么,值可能采取不同行为的不同场景可能是什么。

从答案中我得出以下结论。请确认一下:

a)范围将随着函数作为引用传递而更改,并将获取本地引用,除非上下文未使用bind,call或apply绑定。

b)特别是在回调/异步函数中,如果更改了上下文(例如setTimeout,则上下文会发生变化,因此self的含义会在调用期间发生变化。)

c)它与闭包无关,因为在闭包中检查词法环境是否有评估值,但是,这里评估执行时的上下文

1 个答案:

答案 0 :(得分:1)

self.f是对f函数的引用。

self.f.bind(self)是一个新功能,在调用时调用f selfthis

  

和&之间有什么区别吗?在这种情况下,上面是b并且在那里   a)可能失败的任何情况?

是。行为取决于您如何进行通话:

function me() {}
me.prototype.f = function() {
  console.log(this);
};
me.prototype.operation = function() {
  client.on("this.f", this.f);
  client.on("this.f.bind(this)", this.f.bind(this));
};

var client = {
  on: function(whatever, callback) {
    var self = this;
    setTimeout(function() {
      console.log("=== " + whatever + " ===");
      self.f = callback;

      // `this` not specified
      //  `this.f` logs the global object, or `undefined` in strict mode
      //  `this.f.bind(this)` logs `me` instance
      callback();

      // `this` belongs to current function (passed to setTimeout)
      //  `this.f` logs the global object, or `undefined` in strict mode
      //  `this.f.bind(this)` logs `me` instance
      callback.call(this);

      // `self` is a reference to the client object
      //  `this.f` logs `client`
      //  `this.f.bind(this)` logs `me` instance
      callback.call(self);

      // `this` is determined by the calling object (client again)
      //  `this.f` logs `client`
      //  `this.f.bind(this)` logs `me` instance
      self.f();

    }, 1000);
  }
};


(new me()).operation();

更新

我已经决定在答案中添加更多内容,因为您已经在问题中添加了更多内容。很遗憾,结果很长。

this如何运作

this的值是在函数调用时确定的,而不是在函数定义上确定的。

  • 调用不属于对象this的属性的函数时 将是undefined严格,或引用全局对象 非严格模式。

    function f1() {
      console.log(this === window);
    }
    function f2() {
      'use strict';
      console.log(this === undefined);
    }
    
    f1();  // true
    f2();  // true
    
  • 调用作为对象属性的函数时,this 将引用该对象。

    var object = {
      f: function() {
        console.log(this === object);
      }
    };
    object.f();   // true
    
  • 调用带有new前缀的函数时,this将引用 新创建的对象。

    var backup;
    function Example() {
      backup = this;
      this.value = 0;
    }
    
    var example = new Example();
    console.log(example === backup);  // true
    console.log(example.value);       // 0
    
  • 调用不是对象属性的函数时,但是 是对象原型链的一部分,this将引用 调用对象。

    function Example() {}
    Example.prototype.f = function() {
      console.log(this === example);
    };
    
    var example = new Example();
    example.f();   // true
    

更改this

提到的行为往往不是我们想要的。请考虑以下情况。

    function Counter() {
      this.count;
      this.limit;
      this.interval;
    }
    Counter.prototype.countTo = function(number) {
      this.count = 1;
      this.limit = number;
      this.interval = setInterval(update, 1000);

      function update() {
        console.log(this.count);
        if (++this.count > this.limit)
          clearInterval(this.interval);
      }
    };

    var counter = new Counter();
    counter.countTo(5);   // undefined, NaN, NaN, NaN, NaN, NaN...

调用函数后,

this会获取其值,而不是在定义函数时。稍后将调用interval函数,this将引用全局对象。在大多数情况下,这不是人们所追求的。

隐式更改this

解决setInterval问题的历史方法是将this的引用分配给有问题的函数可见的变量,然后使用该变量。

function Counter() {}
Counter.prototype.countTo = function(number) {
  this.count = 1;
  this.limit = number;

  var self = this;

  function update() {
    console.log(self.count);
    if (++self.count > self.limit)
      clearInterval(self.interval);
  }
  this.interval = setInterval(update, 1000);
};

var counter = new Counter();
counter.countTo(5);

同样采用封闭式,防止污染功能范围。

function Counter() {}

Counter.prototype.countTo = function(number) {
  this.count = 1;
  this.limit = number;

  this.interval = setInterval((function(self) {
    return function() {
      console.log(self.count);
      if (++self.count > self.limit)
        clearInterval(self.interval);
    };
  }(this)), 1000);
};

var counter = new Counter();
counter.countTo(5);

每当创建update时创建新的Counter函数并不是最有效的方法。假设我们想将其移至原型。

function Counter() {
  this.count;
  this.limit;
  this.interval;
}
Counter.prototype.countTo = function(number) {
  this.count = 1;
  this.limit = number;
  this.interval = setInterval(this.update, 1000);
};
Counter.prototype.update = function() {
  console.log(this.count);
  if (++this.count > this.limit)
    clearInterval(this.interval);
};

var counter = new Counter();
counter.countTo(5); // undefined, NaN, NaN, NaN, NaN, NaN...

现在的问题是让update了解self,因为它们不再在同一范围内定义,并且所有{{1}只有一个update函数}第

我们可以创建一个中介函数来包含Counter的值(复制引用)并使用它来进行调用。

this

我们可以使用箭头函数,它在词法上绑定function Counter() { this.count; this.limit; this.interval; } Counter.prototype.countTo = function(number) { this.count = 1; this.limit = number; var update = (function(self) { return function() { self.update(); }; }(this)); this.interval = setInterval(update, 1000); }; Counter.prototype.update = function() { console.log(this.count); if (++this.count > this.limit) clearInterval(this.interval); }; var counter = new Counter(); counter.countTo(5);,也就是说,它的值是根据函数定义确定的,无论它是如何或何时被调用的。

this

明确更改function Counter() { this.count; this.limit; this.interval; } Counter.prototype.countTo = function(number) { this.count = 1; this.limit = number; this.interval = setInterval(() => this.update(), 1000); // neat. }; Counter.prototype.update = function() { console.log(this.count); if (++this.count > this.limit) clearInterval(this.interval); }; var counter = new Counter(); counter.countTo(5);

假设我们的this函数已在其他地方定义,我们希望使用它而不是原型函数。

update

使用前面提到的方法,我们可以在对象或其原​​型中附加对function Counter(to) { this.count; this.limit; this.interval; } Counter.prototype.countTo = function(number) { this.count = 1; this.limit = number; this.interval = setInterval(update, 1000); }; function update() { console.log(this.count); if (++this.count > this.limit) clearInterval(this.interval); } var counter = new Counter(); counter.countTo(5); // undefined, NaN, NaN, NaN, NaN, NaN...的引用,并使用箭头函数绑定update。如果我们不喜欢这种开销,我们可以使用三个强大的功能来明确设置this

在函数上调用this会生成一个新函数,其bind设置为第一个参数。

this

在函数上使用function Counter(to) { this.count; this.limit; this.interval; } Counter.prototype.countTo = function(number) { this.count = 1; this.limit = number; this.interval = setInterval(update.bind(this), 1000); // not bad. }; function update() { console.log(this.count); if (++this.count > this.limit) clearInterval(this.interval); } var counter = new Counter(); counter.countTo(5);call会将apply的调用设置为第一个参数。

this