在 JavaScript 中 call、apply 用來干嘛?
之前我有提到過 call、apply 主要用來改變函數(shù)運行時的執(zhí)行環(huán)境(this),對于執(zhí)行環(huán)境(this)以及 call、apply 用法不了解的,可以向我提問
call、apply 函數(shù)實現(xiàn)分析:
call 函數(shù)實現(xiàn)分析:
語法實現(xiàn):
fun.call(context, arg0, arg1, arg2 ...)
參數(shù)分析:
context:指定 fun 函數(shù)運行時的執(zhí)行環(huán)境(this 值),一般不指定默認(rèn)為null,此時指向全局對象(window || global)。
arg0, arg1, arg2 ...:fun 運行時的具體參數(shù)。
通過分析,我們可以發(fā)現(xiàn),fun 使用 call 方法運行時,會使 fun 的 this 綁定到傳入的 context 對象中,然后拿著之后的參數(shù)運行 fun 函數(shù)。與 初始的 fun() 的區(qū)別在于,改變了 this 的指向。那么如何改變 this 指向呢。我們之前都見過這樣一個例子:
const obj = {
name: 'obj',
sayName: function() {
console.log(this.name)
}
}
obj.sayName() // obj
可以看到,對象內(nèi)部方法在執(zhí)行時 this 是指向 對象本身的。那么我們可以利用這一點,來將要執(zhí)行 call 方法的 fun 的 this 指向到對應(yīng)的對象上,即讓 fun 變成 context 的一個屬性方法,執(zhí)行完畢后,再將該方法 delete 掉,以去掉 context 對于 fun 的影響,代碼實現(xiàn)如下:
Function.prototype.call_test = function(context) {
// 當(dāng) context 未定義或定義為 null 時,將 context 指向 window
context = context || window
// 將要執(zhí)行的 fun(即this) 變成給 context 的 屬性方法
context._callFn = this
context._callFn() // 執(zhí)行屬性方法(即將 函數(shù)的 this 強行關(guān)聯(lián)到 context 對象上)
delete context._callFn // 刪除 對象關(guān)聯(lián)的屬性方法,以去除后續(xù) 對 fun 執(zhí)行的影響
}
const obj2 = {
name: 'test'
}
obj.sayName.call_test(obj2) // test
web前端開發(fā)學(xué)習(xí)Q-q-u-n: 731771211,分享學(xué)習(xí)的方法和需要注意的小細(xì)節(jié),不停更新最新的教程和學(xué)習(xí)方法(詳細(xì)的前端項目實戰(zhàn)教學(xué)視頻,PDF)
可以看到,我們寫的 call_test 方法成功模實現(xiàn)了 call 改變 this 的功能,接下來還要去處理函數(shù)運行時參數(shù)的問題,每個函數(shù)體有一個內(nèi)置的 arguments 變量,用來存儲函數(shù)應(yīng)用時的參數(shù),我們可以使用 arguments 變量來獲取函數(shù)運行時的具體參數(shù):
Function.prototype.call_test = function(context) {
// 當(dāng) context 未定義或定義為 null 時,將 context 指向 window
context = context || window
// 將要執(zhí)行的 fun(即this) 變成給 context 的 屬性方法
context._callFn = this
const args = [...arguments].slice(1) // 首先把 arguments 類數(shù)組變成數(shù)組,再拿到去除第一個參數(shù)的新數(shù)組
context._callFn(...args) // 執(zhí)行包含參數(shù)的屬性方法,將參數(shù)數(shù)組展開為一個個參數(shù)
delete context._callFn // 刪除 對象關(guān)聯(lián)的屬性方法,以去除后續(xù) 對 fun 執(zhí)行的影響
}
const person = {
name: 'person',
say(age) {
console.log(this)
console.log(`我叫${this.name}我今年${age}`)
}
}
const person1 = {
name: 'person1'
}
person.say.call_test(person1, 22) // 我叫person1我今年22
瀏覽器 console 跑了一遍, 666~
apply 實現(xiàn)分析:
語法實現(xiàn):
fun.call(context, [args])
參數(shù)分析:
context:指定 fun 函數(shù)運行時的執(zhí)行環(huán)境(this 值),一般不指定默認(rèn)為 null,此時指向全局對象(window || global)。
[args]:fun 運行時的參數(shù)數(shù)組。
其實 apply 方法跟 call 方法實現(xiàn)沒有多大區(qū)別,唯一就在于對待 參數(shù) 的處理行為,apply 方法要求參數(shù)以數(shù)組或類數(shù)組的形式傳遞(ES5 之后支持類數(shù)組對象),具體實現(xiàn)如下:
Function.prototype.apply_test = function(context) {
// 當(dāng) context 未定義或定義為 null 時,將 context 指向 window
context = context || window
// 將要執(zhí)行的 fun(即this) 變成給 context 的 屬性方法
context._callFn = this
const args = [...arguments].slice(1) // 首先把 arguments 類數(shù)組變成數(shù)組,再拿到去除第一個參數(shù)的新數(shù)組
context._callFn(args) // 執(zhí)行包含參數(shù)的屬性方法
delete context._callFn // 刪除 對象關(guān)聯(lián)的屬性方法,以去除后續(xù) 對 fun 執(zhí)行的影響
}
const person = {
name: 'person',
say(age) {
console.log(this)
console.log(`我叫${this.name}我今年${age}`)
}
}
const person1 = {
name: 'person1'
}
person.say.call_test(person1, [22]) // 我叫person1我今年22(所以參數(shù)較少是,還是用 call 看著舒服點哈~)
web前端開發(fā)學(xué)習(xí)Q-q-u-n: 731771211,分享學(xué)習(xí)的方法和需要注意的小細(xì)節(jié),不停更新最新的教程和學(xué)習(xí)方法(詳細(xì)的前端項目實戰(zhàn)教學(xué)視頻,PDF)
至此,我們完成了 call、apply 方法的手動實現(xiàn)。實現(xiàn)的難點在于,要想到將函數(shù)設(shè)置成 context 的屬性方法,來實現(xiàn) 執(zhí)行環(huán)境(this) 的綁定,調(diào)用完成后需要delete該屬性,以移除影響,不想刪的童鞋可以在瀏覽器里試一試,會出現(xiàn)啥奇葩情況,在這里不展開了。這里只是給大家提供個簡單的實現(xiàn)思路,到實際應(yīng)用中還是要考慮其他問題的,這里暫時也不做展開。感興趣的可以了解下還有那些問題,比如不讓用 ES6 語法怎么辦?比如 ‘_callFn’ 被占用了怎么辦等等,思考是個好習(xí)慣,希望我們的拿來主義少一些。