深入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

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