js系列之实现call, apply, bind函数

888 阅读1分钟

bind 和 call/apply 一样,都是用来改变上下文 this 指向的,不同的是,call/apply 是直接使用在函数上,而 bind 绑定 this 后返回一个函数(闭包) call和apply的唯一区别是call的除了第一个参数是绑定上下文,可以传多个参数,apply的第一个参数是绑定上下文,第二个参数是一个数组 call

Function.prototype.myCall = function(context) {
    // 处理第一个参数传null或者undfined,这时将上下文绑定到window对象上
    context = context || window;
    context._fn = this;
    let args = [...arguments].slice(1);
    let result = context._fn(...args);
    delete context._fn;
    return result;
}

apply

    Function.prototype.myApply = function(context) {
    context = context || window;
    context._fn = this;
    let args = [...arguments].slice(1); // 第二个参数是数组
    let result = context._fn(...args);
    delete context._fn;
    return result;
}

bind

  1. 函数调用,改变this
  2. 返回一个绑定this的函数
  3. 接收多个参数
  4. 支持柯里化形式传参 fn(1)(2)
Function.prototype.myBind = function(context) {
    let self = this;
    let args = [...arguments].slice(1);
    return function() {
        let newArgs = [...arguments];
        return self.apply(context, args.concat(newArgs))
    }
}

但是作为构造函数试用的时候会出错,构造函数的this绑定在实例上,除此之外,还需要解决原型丢失的问题

Function.prototype.myBind = function(context) {
    let self = this;
    let args = [...arguments].slice(1);
    var bound = function() {
        let newArgs = [...arguments];
        // 作为构造函数使用
        if(this instanceof bound) {
            return self.apply(this, args.concat(newArgs))
        } else {
            return self.apply(context, args.concat(newArgs))
        }
    }
    // 维护原型关系
    if(this.prototype) {
        bound.prototype = this.prototype;
    }
    return bound;
}