JavaScript 中的 Bind()、Apply() 和 Call():鮮為人知的差異
每個(gè)開發(fā)者都應(yīng)該充分理解它們的工作原理,并能夠辨別它們之間的微妙差異。
首先要知道,JavaScript 函數(shù)是“一等公民”。這意味著它們都是對(duì)象值——所有函數(shù)都是 Function 類的實(shí)例,擁有方法和屬性:
因此,bind()、apply() 和 call() 是每個(gè) JavaScript 函數(shù)都具備的三個(gè)基本方法。
bind()
你還記得 React 的早期痛苦時(shí)代嗎?當(dāng)時(shí)我們?nèi)栽谑褂妙惤M件,并且事件處理程序通常是這樣寫的:
這只是 bind() 的眾多應(yīng)用之一——一個(gè)嚴(yán)重被低估的 JavaScript 方法。
沒有 bind(),sayName() 會(huì)一團(tuán)糟——alert() 根本不會(huì)生效。
這是因?yàn)?React 在內(nèi)部對(duì)這個(gè)方法做了一些處理,導(dǎo)致 this 的引用在方法內(nèi)部完全被搞亂了。
最初,sayName 顯示 alert 是沒有問題的——就像在這個(gè)類中的另一個(gè)方法一樣:
但是,React 在后臺(tái)對(duì) greet 事件處理程序做了什么呢?它將其重新分配給另一個(gè)變量:
所以 this 發(fā)生了什么?它無法再被找到:
這時(shí)候 bind 派上了用場——它將 this 綁定到你選擇的任意實(shí)例對(duì)象上:
const boundGreet = this.greet.bind(this);
所以我們將函數(shù)綁定到了對(duì)象——也就是 bind 的目標(biāo)對(duì)象。
(我知道正確的說法是“bound”,但讓我們像說“indexes”代替“indices”一樣說“binded”吧)。
bind 是不可變的——它返回綁定后的函數(shù),而不會(huì)改變?cè)己瘮?shù)。
這讓我們可以多次使用它:
對(duì)比 call()
call 和 bind 之間只有一個(gè)很小的區(qū)別。
bind 創(chuàng)建一個(gè)綁定后的函數(shù),可以多次使用。
而 call 呢?它會(huì)立即創(chuàng)建一個(gè)臨時(shí)的綁定函數(shù)并調(diào)用它:
所以 call() 基本上就是 bind() + 一次調(diào)用。
但是當(dāng)函數(shù)有參數(shù)時(shí)怎么辦?該如何處理呢?
完全沒問題——只需將它們作為更多參數(shù)傳遞給 call:
實(shí)際上,你也可以用 bind() 做同樣的事情:
對(duì)比 apply()
一開始你可能會(huì)認(rèn)為 apply() 與 call() 完全一樣:
但是就像 bind() 和 call() 之間有微妙的區(qū)別一樣,apply() 和 call() 之間也有一個(gè)細(xì)微的區(qū)別:參數(shù)的傳遞方式。
一個(gè)記憶技巧可以幫助你記住它們的區(qū)別:
- call() 適用于用逗號(hào)分隔的參數(shù)
- apply() 適用于數(shù)組
總結(jié)
- bind()——綁定 this 并返回一個(gè)新的函數(shù),可以重復(fù)使用。
- call()——綁定并調(diào)用函數(shù),使用逗號(hào)分隔的參數(shù)傳遞。
- apply()——綁定并調(diào)用函數(shù),使用數(shù)組傳遞參數(shù)。
這些函數(shù)方法是理解 JavaScript 函數(shù)和 this 關(guān)鍵字的基礎(chǔ),也是編寫健壯代碼的重要工具。