手写call、apply、bind实现

129 阅读1分钟

call

call接收多个参数,第一个参数为函数上下文也就是this,后边参数为函数本身的参数

let Person={
  name:'Tom',
  say(){
    console.log(this)  
    console.log(`我叫${this.name}`)  
  }
}
Person.say()   //{ name: 'Tom', say: [Function: say] }  //我叫Tom
let Person1={
  name:'Tom1'
}
Person.say.call(Person1)   //{ name: 'Tom1' }  //我叫Tom1

通过第一次打印和第二次打印结果发现,Person1中模拟实现了say方法,并打印出来。

Function.prototype.MyCall = function(context) {
  //context就是demo中的Person1
  console.log(this)   //当前this指向调用Mycall的say方法
  context.say = this //创建一个虚拟的say方法,将其指向say方法
  context.say()
}
// 测试
Person.say.MyCall(Person1)//我叫Tom1

缺陷:call方法支持多参数或者无参数,创建的虚拟方法指定了,函数执行后,没有将创建的虚拟方法删除

实现call

let Person = {
  name: 'Tom',
  say(age) {
      console.log(this)
      console.log(`我叫${this.name}今年${age}`)
  }
}
// Person.say()
let Person1 = {
  name: 'Tom1'
}
// Person.say.call(Person1)

Function.prototype.MyCall = function (context=window,...args) {
  //context就是demo中的Person1
  // 必须此时调用MyCall的函数是say方法,那么我们只需要在context上扩展一个say方法指向调用MyCall的say方法这样this
  console.log(this)
  //为添加的虚拟方法取一个唯一名
  const fn=mySymbol(context)
  context[fn] = this //Mycall里边的this就是我们虚拟的say方法
  context[fn](...args)  //执行fn
  delete context[fn] //删除方法
}
// 测试
Person.say.MyCall(Person1,18) //我叫Tom1

function mySymbol(obj){
  //为添加的虚拟方法取一个唯一值为函数名
  let unique=Date.now().toString(32).slice(0, 8)
  if(obj.hasOwnProperty(unique)){
      //如果该函数名存在递归调用
      return mySymbol(obj)
  }else{
      return unique
  }
}

实现apply

apply与call类似,只是将多参数合并为一个数组

Function.prototype.MyApply = function (context=window,...args) {
  //context就是demo中的Person1
  // 必须此时调用MyCall的函数是say方法,那么我们只需要在context上扩展一个say方法指向调用MyCall的say方法这样this
  console.log(this)
  //为添加的虚拟方法取一个唯一名
  const fn=mySymbol(context)
  context[fn] = this //Mycall里边的this就是我们虚拟的say方法
  context[fn](args)  //执行fn
  delete context[fn] //删除方法
}

实现bind

bind与call,apply区别很大,该方法返回一个新函数,接收多个参数,支持柯里化形式传参

Function.prototype.myBind = function (context = window, ...args) {
  const that = this
  const fn = function () {
    context = this instanceof that ? this : context
    return that.apply(context, [...args, ...arguments])
  }
  if (this.prototype) {
    fn.prototype = Object.create(this.prototype)
  }
  return fn
}