手寫一個 JavaScript 的 apply、call 函數

在 JavaScript 中 call、apply 用來幹嗎?

以前我有提到過 call、apply 主要用來改變函數運行時的執行環境(this),對於執行環境(this)以及 call、apply 用法不瞭解的,能夠參考以前的文章:JavaScript 函數做用域、執行環境(this)、call、apply、bind 的用法javascript

call、apply 函數實現分析:

call 函數實現分析:

語法實現:
fun.call(context, arg0, arg1, arg2 ...)
參數分析:
context:指定 fun 函數運行時的執行環境(this 值),通常不指定默認爲null,此時指向全局對象(window || global)。
arg0, arg1, arg2 ...:fun 運行時的具體參數。

經過分析,咱們能夠發現,fun 使用 call 方法運行時,會使 fun 的 this 綁定到傳入的 context 對象中,而後拿着以後的參數運行 fun 函數。與 初始的 fun() 的區別在於,改變了 this 的指向。那麼如何改變 this 指向呢。咱們以前都見過這樣一個例子:java

const obj = {
    name: 'obj',
    sayName: function() {
        console.log(this.name)
    }
}
obj.sayName() // obj

能夠看到,對象內部方法在執行時 this 是指向 對象自己的。那麼咱們能夠利用這一點,來將要執行 call 方法的 fun 的 this 指向到對應的對象上,即讓 fun 變成 context 的一個屬性方法,執行完畢後,再將該方法 delete 掉,以去掉 context 對於 fun 的影響,代碼實現以下:segmentfault

Function.prototype.call_test = function(context) {
   // 當 context 未定義或定義爲 null 時,將 context 指向 window
   context = context || window
   // 將要執行的 fun(即this) 變成給 context 的 屬性方法
   context._callFn = this
   context._callFn() // 執行屬性方法(即將 函數的 this 強行關聯到 context 對象上)
   delete context._callFn // 刪除 對象關聯的屬性方法,以去除後續 對 fun 執行的影響
}

const obj2 = {
    name: 'test'
}

obj.sayName.call_test(obj2) // test

能夠看到,咱們寫的 call_test 方法成功模實現了 call 改變 this 的功能,接下來還要去處理函數運行時參數的問題,每一個函數體有一個內置的 arguments 變量,用來存儲函數應用時的參數,咱們可使用 arguments 變量來獲取函數運行時的具體參數:數組

Function.prototype.call_test = function(context) {
   // 當 context 未定義或定義爲 null 時,將 context 指向 window
   context = context || window
   // 將要執行的 fun(即this) 變成給 context 的 屬性方法
   context._callFn = this
   
   const args = [...arguments].slice(1) // 首先把 arguments 類數組變成數組,再拿到去除第一個參數的新數組
   
   context._callFn(...args) // 執行包含參數的屬性方法,將參數數組展開爲一個個參數
   delete context._callFn // 刪除 對象關聯的屬性方法,以去除後續 對 fun 執行的影響
}

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 實現分析:

語法實現:
fun.call(context, [args])
參數分析:
context:指定 fun 函數運行時的執行環境(this 值),通常不指定默認爲 null,此時指向全局對象(window || global)。
[args]:fun 運行時的參數數組。

其實 apply 方法跟 call 方法實現沒有多大區別,惟一就在於對待 參數 的處理行爲,apply 方法要求參數以數組或類數組的形式傳遞(ES5 以後支持類數組對象),具體實現以下:app

Function.prototype.apply_test = function(context) {
   // 當 context 未定義或定義爲 null 時,將 context 指向 window
   context = context || window
   // 將要執行的 fun(即this) 變成給 context 的 屬性方法
   context._callFn = this
   
   const args = [...arguments].slice(1) // 首先把 arguments 類數組變成數組,再拿到去除第一個參數的新數組
   
   context._callFn(args) // 執行包含參數的屬性方法
   delete context._callFn // 刪除 對象關聯的屬性方法,以去除後續 對 fun 執行的影響
}

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(因此參數較少是,仍是用 call 看着舒服點哈~)

至此,咱們完成了 call、apply 方法的手動實現。實現的難點在於,要想到將函數設置成 context 的屬性方法,來實現 執行環境(this) 的綁定,調用完成後須要delete該屬性,以移除影響,不想刪的童鞋能夠在瀏覽器裏試一試,會出現啥奇葩狀況,在這裏不展開了。這裏只是給你們提供個簡單的實現思路,到實際應用中仍是要考慮其餘問題的,這裏暫時也不作展開。感興趣的能夠了解下還有那些問題,好比不讓用 ES6 語法怎麼辦?好比 ‘_callFn’ 被佔用了怎麼辦等等,思考是個好習慣,但願咱們的拿來主義少一些。函數

相關文章
相關標籤/搜索