call、apply原理及其实现

116 阅读2分钟

Call

        call()方法使用一个指定的this值和单独给出一个或者多个参数来调用一个函数,其语法使用如下:
        function.call(obj,arg1,arg2,...)

先看如下代码:

    function fn(){
        console.log(this.name);
    }
    
    const obj = {
        name:"findly"
    }
    obj.fn =fn;
    obj.fn();           //  "findly"
    fn.call(obj);       // "findly"

由上面代码可见fn.call(obj) === obj.fn(),由this定义可知谁调用方法,则方法内this指向谁,根据这个原则,可以手写实现自己的call方法:

    Function.prototype.myCall  = function(obj,...args){
      //由上面代码可知,fn调用了call方法,所以call内部this指向fn
      //所以在这需要将fn方法赋值给传入对象的一个方法,实现obj.fn()
      obj.fn = this;
      // 调用将obj.fn()返回
      return obj.fn(...args)
    }
    
    //再次使用上面的fn方法
    fn.myCall(obj); // "findly"

如上,实现了一个简单的自定义call方法,但是并没有考虑到一些细节,如当没有对象调用方法时,方法内部的this应当指向window,所以我们要再一次优化上面代码:

    Function.prototyp.myCall = function(obj,...args){
        //指定this,不传入时为window
        obj = obj||window;
        //将fn作为传入对象的属性方法
        obj._fn = this;
        const result = obj._fn(...args);
        //再使用之后删除该属性方法
        delete obj._fn;
        return result;
        
    }
    fn.myCall(obj); // "findly"

这样一个较健壮的call自定义就实现了;

apply

    apply()方法使用一个指定this值,以及传入数组作为参数,语法如下:
    function.apply(obj,[arg1,arg2,...])

由于applycall函数仅仅是在接收参数的方式不同。基本原理都是一样,所以实现起来也很简单:

    Function.prototyp.myApply = function(obj,args){
        //指定this,不传入时为window
        obj = obj||window;
        //将fn作为传入对象的属性方法
        obj._fn = this;
        const result = obj._fn(...args);
        //再使用之后删除该属性方法
        delete obj._fn;
        //返回执行结果
        return result;
    }
    
    fn.myApply(obj,[]); //"findly"

总结:call()apply()都跟this的指向有关,记住函数内的this指向函数的调用者,就能将其原理掌握就能够自己实现。