js简单易懂手写 call apply bind

183 阅读2分钟

这三剑客的使用容易忘记其原理,总结此笔记加强巩固,有不足之处希望评论指出

1. call使用场景如下:

function people(age,like) {
  console.log(`${this.name} 已经 ${age}岁了 喜欢${like}`)
}
const lili = {
  name: 'lili',
}

people.call(lili, 12, 'music') // lili 已经 12岁了 喜欢music

上面这波操作可以简单理解成如下:

const lili = {
  name: 'lili',
  fn: function people(age) {
    console.log(`${this.name} 已经 ${age}岁了`)
  }
}

people 方法中并没有name属性, 但是通过call,改变了this指向;将people方法挂载到lili这个对象的fn方法上,这样执行fn方法时this指向lili这个对象,所以获得了name, 那么实现自己的myCall 尝试一下

2. 实现myCall

Function.prototype.myCall = function(context) {
  // 若第一个参数未传,则this指向window
  context = context || window 
  // 相当于将people方法挂载到lili这个对象上fn方法上
  context.fn = this 
  const args = [...arguments].slice(1)
  const result = context.fn(...args)
  delete context.fn // 用完就要删除掉
  return result
}

people.myCall(lili, 13, 'food') // lili 已经 13岁了 喜欢food

3. apply使用场景

apply 与 call 的差别在于参数的形式不同, 传入参数是数组形式

people.apply(lili, [14, 'sleep']) // lili 已经 14岁了 喜欢sleep

4. 实现myApply

Function.prototype.myApply = function(context) {
  context = context || window
  context.fn = this
  const args = [...arguments][1]
  const result = context.fn(...args)
  delete context.fn
  return result
}

people.myApply(lili, [15, 'read']) // lili 已经 15岁了 喜欢read

以上就是call 和 apply 的实现 ,接下来实现一下bind,它与上面两个有点不同,因为会涉及new 实例操作

5.bind 使用场景

bind绑定并不是绑定即执行, 如下:

people.bind(lili) // 只是绑定,不会执行

// 先绑定 后面再执行
const bindPeople = people.bind(lili,16,'study') 
bindPeople() // lili 已经 16岁了 喜欢study

由上面操作可以看出bind 返回的是一个函数,下面实现一下

6.实现myBind

Function.prototype.myBind = function(context) {
  if(typeof this !== 'function') {
    throw new TypeError('not a function')
  }
  const that = this
  const args = [...arguments].slice(1)
  // 因为存在判断是否是实例化操作,需要具名
  let Func = function() { 
    // 合并参数,类似处理柯理化函数参数
    const allArgs = [...args, ...arguments] 
    if(this instanceof Func) { // 实例化操作
      return new that(...allArgs)
    } else {
      that.apply(context, allArgs)
    }
  }
  return Func
}
const bindPeople1 = people.myBind(lili,17,'travel')
bindPeople1() // lili 已经 17岁了 喜欢travel

const bindPeople2 = people.myBind(lili,18)
bindPeople2('go hiking') // lili 已经 18岁了 喜欢go hiking

// 若为构造函数呢
const BindPeople = people.myBind(lili,19)

people.prototype.love = 'eat'
const a = new BindPeople('study')
console.log(a.love) // eat 

待续。。。