实现call,bind,apply方法

200 阅读2分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」

call,bind,apply是改变this指向的法宝。

三者的区别是:

call:第一个参数是this,后面的传入的一序列值,立即调用。 apply :第一个参数是this,第二个参数是数组,-立即调用。

bind:第一个参数是this,接收参数可以分次传入,使用了柯里化,返回的是return 的值,需要手动调用执行。

使用方法:

在需要改变this执行的函数后面使用call,bind,apply:

上例子:

    let obj1 = {
        from:'obj1:',
        sayHello(...name){
              console.log(this.from+name)
        }
      }

     let obj2 = {
         from :'obj2:',
        sayHello(...name){
           console.log(this.from+name) 
        }
     }
     console.log(obj2.sayHello('苹果','香蕉'))
     console.log(obj2.sayHello.call(obj1,'苹果','香蕉'))
     console.log(obj2.sayHello.apply(obj1,['苹果','香蕉']))
     let coo = obj2.sayHello.bind(obj1)
     console.log(coo('苹果','香蕉'))                   
    

接下来实现一下call,bind,apply是怎么实现的?

首先实现call:
  Function.prototype.myCall=function(context){
    //  获取待指向的函数,没有默认window
    let ctx = context||window;
    // 赋值当前this //此时的this是需要改变this的函数
    ctx.fn = this;
    // 获取参数,把类数组转换为数组,去掉第一个参数
    let arg = [...arguments].slice(1)
    // 存一份调用的结果
    let res = ctx.fn(...arg)
    // 删除绑定的函数
     delete ctx.fn(...arg)
    //  返回结果
    return res
 }

 obj2.sayHello.myCall(obj1,'苹果','香蕉') //obj1:苹果,香蕉
实现apply :

apply 跟call只是传递的参数不一样,

    Function.prototype.myApply=function(context,arg){
      //定义上下文
      const ctx = context || window
    //   绑定函数
      ctx.fn = this 

     let res = ctx.fn(...arg)
      delete ctx.fn
      return res
    }
    obj2.sayHello.myApply(obj1,['苹果','香蕉'])  //obj1:苹果,香蕉        
     
实现bind

先实现一个简单版,第一次调用绑定this ,第二次调用传递参数。

  Function.prototype.myBind = function(context){
    let that =this

    return function(...arg){
        console.log(arg)
         return  that.myApply(context,arg)
    }
}


obj2.sayHello.myBind(obj1)('雪梨','哈密瓜') //obj1:雪梨,哈密瓜

bind是支持柯里化传递参数的,先处理第一次传递部分参数,第二次传递剩下的参数。 为了支持柯里化,代码还需要修改下:

   Function.prototype.myBind = function(context){
    let that =this
       let par  = [...arguments].slice(1)
       console.log(par)
    return function(...arg){
        console.log(arg)
         return  that.myApply(context,[...par,...arg])
    }
}

obj2.sayHello.myBind(obj1,'圣女果')('雪梨','哈密瓜') // obj1:圣女果,雪梨,哈密瓜

bind有一个特点是可以使用bind的函数new对象。

   function Person(name){
      this.name = name
 }
    Person.prototype.do=function(){
      alert(this.name)
    }
Function.prototype.myBind = function(context){
    let that =this
       let par  = [...arguments].slice(1)

   let fn =  function(...arg){ 
        let ctx = context //默认使用传入this执行的对象
       if(this instanceof fn){ //此时的this是new person('bar')的实例
        ctx =this  
        }
   return  that.myApply(ctx,[...par,...arg])
}
     return fn
}  

let person = Person.myBind(null)

 let p = new person('bar')
  console.log(p.name)   //'bar'
 console.log(p.do()) //  p.do is not a function

还有一个问题,如果使用上面的myBind,使用原型上面的方法会报错,需要把Person构造函数的原型,赋值给内部的fn。

  function Person(name){
        this.name = name
   }

  Person.prototype.do=function(){
  alert(this.name)
}
 
Function.prototype.myBind = function(context){
    let that =this
       let par  = [...arguments].slice(1)

   let fn =  function(...arg){ 
        let ctx = context //默认使用传入this执行的对象
       if(this instanceof fn){ //此时的this是new person('bar')的实例
        ctx =this  
        }
   return  that.myApply(ctx,[...par,...arg])
}
    fn.prototype =this.prototype
     return fn
}  

let person = Person.myBind(null)

 let p = new person('bar')
  console.log(p.do())
  

完。