前言
這篇文章我們要討論三個函式 (call、apply、bind),這是用來控制特殊關鍵字 this 指向的函式。
call()、apply()、bind()
在前面的篇章提到,在執行環境中有變數環境、外部參考,還有 JavaScript 幫我們設定好的 this
變數。
我們已經看過 this
預設指向全域物件、也可以指向包含函式的物件,然而之前我們提到控制 this
的方式,是使用一個變數來儲存 this
指向的位址。
而這一篇提到的三個函式將幫助我們更有效率的控制 this
。
函式是特殊形態的物件,它具有
- 名稱屬性 - 可匿名的名稱屬性
- 程式屬性 - 可包含程式碼,而程式屬性是可以被呼叫的,所以可以被執行。
函式就是物件,所以函式可以有屬性和方法,而且所有函式都有 call()、apply()、bind() 方法
bind()
我們先建立一個初始的範例,如下:
1 | var person = { |
- 我們建立了
person
物件,並且在裡面建立了getFullName
方法。 - 方法內的
this
指向person
物件,並回傳fullName
。 - 宣告了
logName
並指向一個匿名函式,但函式內的this
此時是指向全域物件windows
,因此執行logName
函式後得到undefined
。
這時就是 bind()
出場的時候了,將程式碼改成以下:
1 | var person = { |
我宣告了 logPersonName
並且取用 logName
的 bind
方法,然後傳入想要 this
變數指向的物件,接著 bind
方法會回傳一個新的函式,它會複製 logName
函式,並且設定為一個新的函式物件。
所以當呼叫 logPersonName
函式時,因為 this
已經指向 person
物件,所以輸出就如同預期。
甚至也可以寫得更簡潔,像是這樣:
1 | var person = { |
bind
方法會產生 logName
函式的拷貝,並且將 this
指向為我們指定的物件。
1 | var person = { |
bind
方法會將函式完整的拷貝下來。
這就是 bind 的作用,創造拷貝函式,然後將 this 指向到某個物件。
call()
接續上面的範例
1 | var person = { |
call
方法可以讓我們決定 this
要指向哪個物件、也可以傳入參數,而第一個參數就是決定 this
要指向的物件。基本上就跟 () 用法一樣,但 call
方法允許控制 this
的值。
與 bind
方法不同的地方是,call 方法並不是創造函式的拷貝並等待呼叫,而是直接執行並且改變 this 的指向。
apply()
接續上面的範例
1 | var person = { |
apply
方法與 call
方法雷同,只是控制 this
之後的傳入參數,部分略有不同。
call
方法允許傳入各種型別的值,但是apply
方法只接受陣列作為參數。
所以這兩個方法可以根據函式的情況來使用。
不使用括號來達成立即執行函式
看過了 apply
方法與 call
方法,我們可以這麼寫:
1 | (function(lang1, lang2){ |
所有的函式都可以使用 apply、call、bind
方法。
實際上的使用
這些方法可以如何地在實際開發上使用呢?
函式借用 (function borrow)
1 | var person = { |
我們創造了兩個物件,一個 person
物件、一個 person2
物件,但是在 person2
物件內沒有 getFullName
方法。
可以透過 call
、 apply
方法達成函式借用,即使 person2
物件內沒有 getFullName
方法,透過改變 this
的指向,也可以輸出 person2
的物件內容。
函式柯里化 (function curring)
這個部分跟 bind
方法的特性有關,如果我們傳入參數給 bind,會有不太一樣的事情發生,讓我們來觀察。
1 | function multply(a, b){ |
我寫了一個會回傳 a * b
的 multply
函式,並且使用 bind
方法拷貝一份新的函式給 mulipleByTwo
,在此 this
的指向不重要,重要的是傳入的參數。
bind
方法會將傳入的參數設定為一個定值。
以例子來說 multply.bind(this, 2)
就好比這樣:
1 | function multply(a, b){ |
而在 bind
內設定了傳入的參數後,我們呼叫的函式所帶入的參數就會變成另一個,以本例來說就是 b
。
因此輸出就是 2 * 4 = 8
如果 bind 填入了所有參數呢?
1 | function multply(a, b){ |
像這樣,使用 bind
方法拷貝一份新的函式給 mulipleByThree
,並且填入 a
跟 b
參數。
當我們呼叫 mulipleByThree
帶入參數時,受到 bind
影響,所以無論帶入什麼,輸出的結果都是 12 。
於是 函式柯里化 (function curring) 的意思就是建立一個函式的拷貝,並設定不可變的預設參數。
函式柯里化主要用於數學的運算,可以寫個基本的函式,然後根據這個函式放入預設參數,用以減少需要填入的參數,這是一種 bind 的用法。