call、bind and apply
這堂課將介紹 3 個很令 JavaSCript 初學者困擾的內建函式 call()
、apply()
和 bind()
複習一下之前學過的觀念,在執行一個 funciton
時,會產生一個 Execution Context,並自動生成 Variable Environment、Outer Environment 和關鍵字 this
。
另外,函式被建立時還會有以下特性
- 屬性
property
- NAME: 如果是
Anonymous Function
就不會有 - CODE: 在函式中可被呼叫執行的程式碼
- NAME: 如果是
- 方法
method
- 3 個內建函式:
call()
、apply()
和bind()
- 3 個內建函式:
範例程式碼
接著來看一段範例程式碼如下:
首先,我們建立一個 object,包含一些 property,並透過 method 回傳自己的 property
1 | var person = { |
結果當然會出現 error,因為函式 logName
中的 this
會指向 window
,而 window
並不是物件 person
,所以沒有 getFullName
這個 method
bind()
那麼如果我們要讓 logName
函式指向物件 person
,這時候就可以使用到 bind()
,但這裡要注意的是, bind
前的函式 logName
不能加上括號 ()
呼叫它,否則就會直接 return
結果了,我們要的只是這個函式 ; 接著 bind
中的參數放入 person
,這麼一來在執行新的函式 logPersonName
時,其實就是執行 logName
,只是其中的 this
會指物件 person
。
1 | var person = { |
然而,我們其實還可以直接在建立函式 (creates a function on the fly) 時,就透過 bind()
來把 this
指向目標物件,並回傳一個 function
,直接呼叫這個 function
就會得到和剛才一樣的結果。
1 | firstname: 'John', |
如剛才所說,新建立的函式 logPerson
其實就是執行 logName
函式,所以帶入的參數也會帶入函式 logName
中。
1 | var person = { |
call()
接下來看看透過 call()
我們可以怎麼做。
首先,call()
其實和括號 ()
一樣會執行這個 function
,但不一樣的是,第一個參數是指向的 this
,後續才是帶入的參數。
1 | var person = { |
apply()
而 apply()
的用法就和 call()
就很像了,不同的是,第二個參數是一個 array
,array
中的值就是依序帶入的參數了
1 | logName.apply(person, ['en', 'es']); |
而我們也可以透過 IIFE
的方式建立函式 creates a function on the fly
1 | (function(lang1, lang2) { |
Function Borrowing
延續剛才的範例,我們建立第二個物件 person2
,但它沒有 getFullName
這個 method
,這時候我們就可以透過剛剛學過的 bind()
、Call()
和 apply()
來借用 person
的 method
。
1 | var person = { |
Function Curring
接著,我們來看一下第二個不同的範例,這個範例將使用到 bind()
; 前面有提到 bind()
只會回傳 function
,call
和 apply
則會直接執行 function
。
首先,我們建立一個函式並將傳入的兩個變數相乘,接著透過 bind()
來建立第二個函式,但不同的是,透過 bind()
建立時,我們並沒有給足兩個參數
1 | function multiply(a, b) { |
在 bind()
中給的參數其實就會成為新建函式的預設值,剩餘的參數才會在呼叫時補上
1 | function multipleByTwo(b) { |
如果我想要一開始就給足參數的數量,那麼後須呼叫時帶入的參數就不會影響執行的結果了
1 | var multipleByTwo = multiply.bind(this, 2, 3); |
這種先創立一個 function
後,再透過複製並給予預設參數的方式就稱為 Function Curring
,實際開發很常應用在大量的算術上。