apply
call
bind
這三個函數可以改變 this
的綁定,語法上有略微差別,可以看我很早之前的一篇文章做過簡單的介紹 淺談JavaScript中的apply、call、bind。
this 優先級
-
new
關鍵字。函數被new
調用,this
指向由new
新構建出來的這個對象 - 函數通過
apply call bind
調用,this
指向綁定的對象 - 函數被調用時,
this
指向調用的對象 - 默認。非嚴格模式情況下,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
對象作爲 context
的 key
,並把 this
賦值給他。此時的 this
值爲 getName
,因爲是 getName
調用了 myApply
,所以 this
指向調用者。
當我們以 context[key](...args)
這種方式調用 getName
時,this
指向了 context
,因爲是 context
調用了 getName
方法。藉此完成了 this
值得轉換。
call
和 bind
同理
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)
}
}