web前端入門到實戰(zhàn):手寫一個 JavaScript 的 apply、call 函數(shù)

在 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í)慣,希望我們的拿來主義少一些。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容