call / apply / bind

对于 call / apply / bind 来说,他们的首要目的是用于改变执行上下文的 this 指针。

call / apply

对 call / apply 的使用,一般都如下,用于改变执行环境的上下文。只是 call 接受的是一个一个的参数,而 apply 则是接受的是一个参数数组。

const obj1 = {
  a: 1,
  myFunc(var1) {
    console.log(this.a + var1)
  }
}
const obj2 = {
  a: 2
}

const myFunc = obj1.myFunc

myFunc(1)                 // NaN
obj1.myFunc(1)            // 2
myFunc.call(obj2, 1)      // 3
myFunc.apply(obj2, [1])   // 3

bind

bind 是 ES2015 出的一个方法,也是用于改变函数内部的 this 指向。但不一样的是,bind 方法不是直接执行的,而是生成一个新的已被改变过的函数。

const obj1 = {
  a: 1,
  myFunc(var1) {
    console.log(this.a + var1)
  }
}
const obj2 = {
  a: 2
}

const myFunc = obj1.myFunc
const bindMyFunc1 = myFunc.bind(obj1)
const bindMyFunc2 = myFunc.bind(obj2)

myFunc(1)         // NaN
bindMyFunc1(1)    // 2
bindMyFunc2(1)    // 3

通过上面的例子就可以看出来,bind 方法就可以生成一个新的 this 指向的 function。

手动写 bind 函数

仅仅作为简单实现的话,我们仅需要注意改变 this 指向和预置参数即可。

function bind(fn, _this, ...args) {
  if(typeof fn !== 'function') {
    throw new Error('bind fn need to be function')
  }
  return function(...innerArgs) {
    return fn.apply(_this, [...args, ...innerArgs])
  }
}

当然这个手动实现的 bind 方法是只实现了最主要的功能,对函数的原型链和作为构造函数的方式都是没有考虑到的。这里可以参考 MSDN 的 polyfill 方法。

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() {
          // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
          return fToBind.apply(this instanceof fBound
                 ? this
                 : oThis,
                 // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 维护原型关系
    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();

    return fBound;
  };
}
posted @ 2019-04-10 14:20  Yika丶J  阅读(201)  评论(0编辑  收藏  举报