深入JavaScript之模拟 apply, call, bind

apply call bind 这三个函数可以改变 this 的绑定,语法上有略微差别,可以看我很早之前的一篇文章做过简单的介绍 浅谈JavaScript中的apply、call、bind

this 优先级

  1. new 关键字。函数被 new 调用,this 指向由 new 新构建出来的这个对象
  2. 函数通过 apply call bind 调用,this 指向绑定的对象
  3. 函数被调用时,this 指向调用的对象
  4. 默认。非严格模式情况下,this指向window, 严格模式下,this指向undefined。

总结一下优先级
new 关键字 > apply call bind > 函数调用 > 默认

实现

我们既然要重写 apply,那前两个优先级是用不了的。我们使用第三个优先级——函数调用——改变 this 的指向。

先看下代码

Function.prototype.myApply = function (context, args) {
  context = context || window
  args = args || []
  const key = Symbol()  // 保证key的唯一性,避免覆盖原有的属性方法
  context[key] = this   // 此时的this指向myApply的调用者
  const result = context[key](...args)
  delete context[key]   // 删掉新建的key
  return result
}

上面的代码忽略一些边界情况,实现了简易版的 apply
看下使用的效果

const person = {
  name: 'A',
  getName(prefix, suffix) {
    return `${prefix} ${this.name} ${suffix}`
  }
}

const result = person.getName.myApply({name: 'apply'}, ['one', 'two'])

console.log(result)  // one apply two

首先我们创建了一个 Symbol 对象作为 contextkey,并把 this 赋值给他。此时的 this 值为 getName,因为是 getName 调用了 myApply,所以 this 指向调用者。
当我们以 context[key](...args) 这种方式调用 getName 时,this 指向了 context,因为是 context 调用了 getName 方法。借此完成了 this 值得转换。

callbind 同理

call

Function.prototype.myCall = function (context, ...args) {
  context = context || window
  const key = Symbol()
  context[key] = this
  const result = context[key](...args)
  delete context[key]
  return result
}

bind 的实现可以使用我们写的 call 函数。需要注意的是 bind 可以传参,新生成的函数也可以传参。

Function.prototype.myBind = function (context, ...args) {
  const self = this
  return function newFn(...newFnArgs) {
    return self.myCall(context, ...args, ...newFnArgs)
  }
}

源码

https://github.com/chaohangz/test/blob/master/bind.js

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章