[JavaScript] call 與 bind 雖然相似 卻只有bind 可以做到 Currying ,那麼問題來了,什麼是 Currying ?

realdennis
5 min readDec 18, 2018

Function.prototype.bind 提供的好處,不只是為了一個函數做一個綁定 this 範疇的包裹,其最廣為人知的做法是能將一個函數做「科里化」,這篇文章會從 bind 與 call 相似的語法格式說起,並且討論科里化的好處。

此篇接續上一篇的 [JavaScript] 聊聊call、apply、bind的差異與相似之處 ,我們可以明白 call 、 apply 、 bind 三者的差異性以及相似程度,但針對 bind 我們僅僅只討論第一個參數能做到的事,這一篇文章想做的 — 詳細說明 bind 語法格式,進而討論 bind 的更多樣化用法,以及 Functional Programming 的傳教僧常說的Currying。

然而 Currying 跟 bind 有絕對的關係嗎?我們先留個伏筆。

我們先來看看 call 與 bind 的語法格式

call 的語法格式

fun.call(thisArg[, arg1[, arg2[, ...]]])

bind 的語法格式

fun.bind(thisArg[, arg1[, arg2[, ...]]])

可以發現說 call 與 bind 的語法格式幾乎一樣。

而其根本差異性是 call 會直接去執行函數並回傳結果,而 bind 回傳的是一個「被設定好的函數」,這也意味著 bind 不只能做到 wrapper 特定 this ,還能去 wrapper 參數。

而這個「設定」兩字正是 bind 的精髓所在。

這裡整理兩篇以來提到的bind用法,皆是從這句話所衍生

  1. bind 可以「設定」 this
  2. bind 可以「設定」參數

透過第二點的好處我們可以輕鬆做到簡單的 Currying 。

我們把 talking函數 不斷做設定 實現curry

實現簡單的currying

Currying — 科里化,到底是要科里什麼東西

先定義科里 再來討論科里,我們來看看維基百科的定義:

計算機科學中,柯里化(英語:Currying),又譯為卡瑞化加里化,是把接受多個參數函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受餘下的參數而且返回結果的新函數的技術。

語畢,你發現其實上面做的事很明顯的在完成 currying 這個動作,用一個口語化的方式來解釋,科里化想做的只是把函數的接口做封裝,也就是上面說的將其他參數先做設定,接口單純。

然而用bind去做curry還是會有它的限制所在,舉例:我們沒有辦法把特定index的參數給bind進去。

f(x,y,z) →無法針對y變數去設定

有一個很簡單,小孩子也寫得出來的solution,就是重新封裝一個新的函數。

小孩子表示:

Arrow Function — 箭頭函數的救贖

從上述的例子可以看到,這類的函數,就如同高中數學所學的函式推演,讓我們來看看最簡單的函數的題目,並想象怎麼「題意」化為程式語言。

題目:當 f(x,y,z) = 2x + y + 3z,

如果 g(x,z) = f(x,3,z); 請問 g(2,3) =?

g(2,3) = 2*2 +3 +3*3 = 16 #

把題目打上去,答案就出來了,有夠賺耶。
尼瑪

箭頭函數可以直接忽略數學運算上不在乎的「動態this」,透過數學化的方式撰寫函數,這樣子的程式風格如同仰賴數學題意說明,無論的定義或是應用,以及回傳結果。

透過不停的封裝,我們可以參數預設定,實現「科里化」。

來下一個 conclusion 吧,從這篇廢文可以知道一件事。

  1. bind 方法不只可以綁定 this 其接口與 call 方法一樣
  2. bind 方法可以設定函數,於是可以實作 Currying
  3. Currying 不需透過 bind 去實現
  4. 箭頭函數能讓你忽略動態 this 實現如同數學運算時的 Coding Style

另外

  1. bind 會控制 this 很不方便
  2. bind 無法預設定特定參數 很不方便

寫在最後

因為有太多文章把 bind 跟 curry 放在一起做討論,本篇標題也故意透過這種方式下題。

看到最後你會發現 — 其實 Currying 就在身邊,不要想得太複雜。

--

--