js中call、apply、bind的简单实现

352 阅读2分钟

其实大家都会使用call、apply、bind来修改this的指向,但是第一次在面试中提到让我实现一下这几个function,我是有点懵逼的(前端小白)。 在《你不知道的JavaScript》上卷一书中详解了关于this的指向问题。我在思考实现的时候没有看具体的源码,就是从这本书列举的this绑定的优先级来考虑。 如果有错误,请大佬指教。

列举一下this绑定的优先级规则:

  • new调用?绑定到新创建的对象
  • call、apply、bind调用?绑定到指定的对象
  • 由上下文调用?绑定到上下文对象
  • 默认:严格模式?undefined : 全局对象

例外情况

  • call、apply、bind调用时,如果指定的对象为null、undefined,使用默认绑定规则
  • 箭头函数:书里用this词法的概念,如果是初入前端,对词法类的概念不太理解的话,可以简单理解为定义箭头函数的的this,即外层作用域来决定this。我理解的就是直接使用父级作用域的this。

我考虑的过程很简单,this的优先级由上面四个顺序决定,那就直接使用上下文调用来实现就好。也就是将函数作为指定对象的一个属性,使用该对象来调用函数。

    // call实现
    function _call() {
        let args = [...arguments];
        let context = args.shift();
        context.func = this;
        const result = context.func(...args);
        delete context.func;
        return result;
    }
    // apply的实现
    function _apply() {
        let args = [...arguments];
        let context = args.shift();
        context.func = this;
        const result = context.func(...args[0]);
        delete context.func;
        return result;
    }
    // bind的实现
    function _bind() {
        let args = [...arguments];
        let context = args.shift();
        context.func = this;
        const result =  function() {
           const result = context.func(...args);
            delete context.func;
            return result
        }
        return result;
    }
    Function.prototype._call = _call;
    Function.prototype._apply = _apply;
    Function.prototype._bind = _bind;

下面是写的简单测试代码

    function foo() {
        return this.name;
    }
    
    const person = {
        name: "Jack",
        age: 23
    };
    const result_call = foo._call(person, "1", "2");
    const result_apply = foo._apply(person, ["3", "4"]);
    const result_bind = foo._bind(person, "5", "6");
    console.log('result_call :', result_call); // Jack
    console.log('result_apply :', result_apply); // Jack
    console.log('result_bind :', result_bind); 
    // function() {
    //   const result = context.func(...args);
    //    delete context.func;
    //   return result
    //}

与call、apply不同,bind的返回的是一段硬编码的函数。 bind实现,可以直接使用call和apply,我这里写的代码重复。 以上代码使用了es6的语法,只适用于简单理解,不适合实际开发。源码中的实现比这里要复杂。 遗留一个问题:关于柯里化的实现和原理,我一直没有弄明白。今天写bind的实现的时候,感觉柯里化基本就是使用闭包。等我研究一下,出个详解。