手写call、apply、bind

131 阅读2分钟

call的实现

Function.prototype.myCall = function () {
    let [context,...args] = [...arguments]
    context =  context || window // 如果不传context,非严格模式下浏览器环境会指向window
    context.fn = this // 借用this对应的方法
    let result = context.fn(...args)
    delete context.fn // 调用该方法后清除该上下文环境上的方法
    return result
}

apply的实现

apply其实与call的实现差不多的,只是apply的第二个参数为一个数组

Function.prototype.myApply = function (context){
    let args = arguments[1]
    context.fn = this
    let result
    if(args){
        result = context.fn(...args)
    }else{
        result = context.fn()
    }
    delete context.fn
    return result
}

bind实现

call、apply是直接返回的改变上下文函数执行的结果,bind是返回绑定了上下文的函数

Function.prototype.myBind = function (){
    const that = this
    let [context,...rest] = [...arguments]
    return function Fn(...args){ //考虑new的情况。MDN解释:当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。不过提供的参数列表仍然会插入到构造函数调用时的参数列表之前。
        if(this instanceof  Fn) {
            return new that(...rest,...args)
        }else{
            that.apply(context,[...rest,...args])
        }
    }
}

如果只是实现bind的基本功能,可以简写为一行代码:

const bind = (fn, context , ...rest) => (...args) => fn.apply(context, [...rest, ...args])
// 写法参考30secondsofcode

MDN bind方法的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.bind()时, this为Function.prototype 
      // this.prototype(即Function.prototype.prototype)为undefined
      fNOP.prototype = this.prototype; 
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();

    return fBound;
  };
}