这篇模手写call、apply、bind学不会,我回家养猪仔

138 阅读2分钟

R-C.jpg

手写call分析

function foo(name, age) {
  console.log(this, name, age); 
}

foo.call({a: 1}, 'bb', 10) // {a: 1} 'bb' 10

  1. foo.call() 把foo当做一个对象,然后调用foo对象中call方法,我们每个函数都有call方法,所以我们可以直接调用foo.call() 原理是 Function.prototype.call = function () {}

  2. call方法的第一个参数是foo的this指向,后面的参数是foo函数的参数

  3. 改变this指向原理: 我们给content对象的fn属性 赋值 foo函数,然后调用content.fn(),这样foo函数中的this就指向了content

实现myCall

// 1、每个函数对象都能调用myCall函数,所以我们可以把myCall函数挂载到Function.prototype上,且有2个参数,第一个参数是this指向,第二个参数是foo函数的参数
  Function.prototype.myCall = function (content, ...args) { 
  // 判断content是否为null或者undefined,如果是,那么this指向window,如果不是,那么this指向content(兼容 第一个参数为其他的情况,可以省略)
  content = (content === null || content === undefined) ? window : Object(content)
  // 3、给content对象的fn属性赋值为foo函数, 调用content.fn(),这样foo函数中的this就指向了content
  content.fn = this;
  content.fn(...args)
  // 删除content对象的fn属性 (多余属性,删除)
  delete content.fn
  }

手写apply分析

function foo(name, age) {
  console.log(this, name, age);
}

foo.apply({a: 1}, ['bb', 10]) // {a: 1} 'bb' 10

  1. call 和 apply的第一个参数都是this指向, 不同的是apply的第二个参数是一个数组,而call的后面参数是一个一个的传递,其他都相同
  2. 只需要把的第二个参数改成数组就可以了

实现myApply

  Function.prototype.myApply = function(content, args) {
      content = (content === null || content === undefined) ? window : Object(content)
      content.fn = this
      content.fn(...args)
      delete content.fn
    }

`基于myCall优化 myApply

  Function.prototype.myApply = function(content, args) {
    this.myCall(content, ...args) // 同 this.call(content, ...args)
  }

手写bind分析

  1. call和bind 第一个参数都是this指向, 后面的参数都是函数的参数, 不同的是call是直接调用函数, bind是返回一个函数
  2. 我们只要把call中的内容 以函数的方式返回出去就好了
  3. 返回的函数还可以传递参数, 把args 和 newArgs 拼接起来就好了

实现myBind

Function.prototype.myBind = function(content, ...args) {
  content = (content === null || content === undefined) ? window : Object(content)

  return (...newArgs)=>{
    content.fn = this
    content.fn(...args, ...newArgs)
    // delete content.fn  这里能删除吗? 不能删除,因为返回的函数可能会被多次调用,如果删除了,那么第二次调用的时候,content.fn就不存在了
  }
}

let tt = foo.bind({a: 1}, 'bb')
tt(20)
tt(20)
tt(20)
tt(20)

myCall、myApply、代码优化

  1. 上述,已知myCall、myApply只是第一个参数后的 入参格式不同,我们可以把公共代码抽出

  2. 把共代码封装成exeFn函数,挂载到Funtion.prototype上

优化后的代码如下:


// 封装exeFn挂载到Funtion.prototype上
Function.prototype.exeFn = function (context, ...args) {
  context = (context === null || context === undefined)  ? window : Object(context)
  context.fn = this;
  context.fn(...args)
  delete context.fn
}

// 手写myCall

Function.prototype.myCall = function (context, ...args) {
  this.exeFn(context, ...args)
}

// 手写apply
Function.prototype.myCall = function (context, args) {
  this.exeFn(context, ...args)
}