call、apply、bind的源码理解小笔记

173 阅读2分钟

call

  • 在方法调用中,this 总是指向调用它所在方法的对象,this 的指向与所在方法的调用位置有关,而与方法的声明位置无关(箭头函数this永远指向它所在的作用域
  • 下面是一个小栗子🌰
const myObj = {
  number: 123
}

const fn = function () {
    console.log(this.number)
}

fn() // 输出 undefined
// 此时 fn 的 this 指向了window, 并不能获取到 number 属性
  • 换一种写法
const myObj = {
  number: 123,
  fn: function () {
    console.log(this.number)
  }
}

// 此时 fn 的 this 指向了 myObj 并能获取到 number
myObj.fn() // 输出 123
  • call 实现的方法
Function.prototype.mycall = function (thisArg) {
 // 调用 call 方法的必须是一个函数
 if (typeof this !== 'function') {
   throw TypeError('Error')
 }
 const fn = Symbol('fn')
 // 获取参数
 const arg = [...arguments].splice(1)
 thisArg = thisArg || window
 // 将调用 call 的函数对象存入 thisArg 的 fn 属性中
 thisArg[fn] = this
 // 执行调用 call 方法的函数
 const result = thisArg[fn](...arg) // 此时 this 指向了 thisArg
 // 执行结束后删除该临时属性
 delete thisArg[fn]
 // 返回结果
 return result
}

apply

  • 与 call 唯一区别就是传参方式的不同
Function.prototype.myapply = function (thisArg) {
 // 调用 apply 方法的必须是一个函数
 if (typeof this !== 'function') {
   throw TypeError('Error')
 }
 const fn = Symbol('fn')
 // 获取参数
 const arg =   const arg = arguments[1]
 thisArg = thisArg || window
 // 将调用 call 的函数对象存入 thisArg 的 fn 属性中
 thisArg[fn] = this
 // 执行调用 call 方法的函数
 const result = thisArg[fn](...arg) // 此时 this 指向了 thisArg
 // 执行结束后删除该临时属性
 delete thisArg[fn]
 // 返回结果
 return result
}

bind

  • call、apply、bind 本质都是改变 this 的指向,不同点 call、apply 是直接调用函数,bind 是返回一个新的函数
Function.prototype.myBind = function (thisArg) {
 // 绑定的 this 必须为 function
 if (typeof this !== 'function') {
   throw TypeError('Error')
 }
 // 保存 this
 const self = this
 // 获取参数 传递给调用者
 const arg = Array.prototype.splice.call(arguments, 1)
 // 一个空函数保存原函数的 prototype
 const epFn = function () {}
 // 箭头函数没有 prototype
 // 箭头函数的 this 永远指向它所在的作用域
 if (this.prototype) {
   epFn.prototype = this.prototype
 }
 // 需要被返回的绑定后的函数
 const bound = function () {
   return self.apply(
     // 判断是否通过 new 来调用 bound
     // 函数作为构造函数用 new 关键字调用时,不应该改变其 this 指向,因为 new绑定 的优先级高于 显示绑定 和 硬绑定
     this instanceof epFn ? this : thisArg,
     // 合并参数
     arg.concat(Array.prototype.splice.call(arguments))
   )
 }
 // 修改绑定函数的原型指向,使其指向原函数
 bound.prototype = new epFn()
 return bound
}