## js中的函數柯里化(Currying)
> 在計算機科學中,柯里化(Currying),又譯為卡瑞化或加里化,是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回接受余下的參數而且返回結果的新函數的技術。
>
> -- [維基百科](https://en.wikipedia.org/wiki/Currying)
>
### 從一道面試題談談函數柯里化
> 題目:使用js實現 add(1)(2)(3)(4) 返回10
>
初次看到這個題目時,我想到的是閉包,因為閉包的定義是:定義在一個函數內部的函數,靜態保存所有了父級作用域的內部函數。所以每次的值可以保存住,到了最后可以全部訪問到。但是想到使用閉包,不知道如何去寫,如果按題所示,不要求后續有無窮,所以可以多嵌套幾層,但是當問題無限擴大時,就不是嵌套可以解決的了,于是我去百度搜索許多關于這方面的知識,今天進行一次總結。
通過函數柯里化的概念,我們可以對其進行處理。
當然下面是初始處理,并不是真正的柯里化。為了方便理解,逐步深入。
```js
//首先我們需要一個入口函數進行基本處理
var add=function(a){
//定義一個保存所有傳入的參數的數組,如果結果是為了求和,則
var result=[];
// 將第一個傳入的參數放置進去
result.push(a);
// 定義函數 _add 引用result變量,使其保持不被銷毀掉
var _add=function(b){
// 將參數push進入result中去
result.push(b);
// 返回函數 _add 進行下次一調用
return _add;
}
// 將原生自帶的toString 進行重寫
_add.toString=function(){
console.log(result);
return result;
}
// 返回 _add 函數進行下次調用
return _add;
}
```
執行結果
```js
>>> add(1)
>>> [1]
>>> ? #<Function>
>>> add(1)(2)(3)
>>> (3)?[1, 2, 3]
>>> ? #<Function>
>>> let a=add(1)(2)(3)
>>> a
>>> (3)?[1, 2, 3]
>>> 如果沒有重寫 toString 方法時
>>> add(1)
>>> ? (b) {
>>> result.push(b);
>>> return _add;
>>> }
```
上面函數經過測試,發現返回了result的數組,在這個例子中,最主要的就是 `_add` 函數的問題,如何在調用結束后返回最終的 `result` 值,經過搜索查詢發現了兩個特殊方法,是我們平時最常見的 `toString` 和 `valueOf` 兩個函數,關于這兩個函數的相關信息[鏈接在此](https://segmentfault.com/a/1190000011853909),如果沒有重寫 `toString` 則會輸入函數內容,而不是結果。
在不賦值進行調用時會在游覽器控制臺輸出一個 `? #<Function>` ,目前不知道是什么原因。有知道的麻煩聯系我。當然我在其他博客看到的也有其他一些不知名的提示,可以和下面博客參考著來看 [地址](https://www.cnblogs.com/daisykoo/p/5569619.html)

### 真正的函數柯里化
> 柯里化是將接受多個參數轉換為接受一個單一參數來看。
其實在上面的例子中,也可以將所有參數放置進入一個函數的參數中來進行處理,也算是柯里化,但是我們這個例子講真正的js柯里化。
假設你有一個儲錢罐 `countMoney` 函數,和一個記錄本 `arr` 數組,當你每月有空錢時進行儲存,每次在 `arr` 中記錄一次,存入儲錢罐中
```js
var arr=[];
var countMoney=function(arr){
var sum=0;
for(var i=0;i<arr.length;i++){
sum+=arr[i];
}
return sum;
}
arr.push(1);
arr.push(2);
countMoney(arr);
```
可以通過這種方式來進行存儲,但是有本記錄,是會被發現的,所以這個時候,我們想是否可以這樣
```js
// 每次存儲是調用一次,不需要再次記錄下來
countMoney(1);
countMoney(2);
// 等到真正需要的時候我們可以直接計算出來這個總值
countMoney(); //3
```
于是問題解決的方式變為柯里化問題,需要將多個參數接受轉換為接受單一參數的問題。
于是我們可以使用下面的方式進行處理
```js
var countMoney = (function() {
let moneys = 0;
let arr = [];
var result = function() {
// 判斷是否還有參數,如果沒有,則返回存儲起來值的總和
if(arguments.length == 0) {
for(var i = 0; i < arr.length; i++) {
money += arr[i];
}
return money;
} else {
// arguments 是個類數組來著,應該用展開符展開才能push進去
// 通過arguments 處理可以傳入多個參數值
console.log(...arguments)
arr.push(...arguments);
return result;
}
}
return result;
})();
countMoney(1)(2)(3)
countMoney()
6
```
上面的例子完全可以實現柯里化,并且進行擴展,現在可以安全的存放錢了。
實際上,在JavaScript的很多思想和設計模式中,閉包是個很常見的且很重要的東西,上面兩個例子代碼中,本質上就是利用了閉包。上面的 `countMoney` 函數是個立即執行的函數,返回一個新函數,而這個新函數實際上就是一個閉包,這個新函數把每次接收到的參數都存儲起來,并且繼續返回一個新函數,當發現某次調用時沒有傳入參數,那就意味著要進行數據統計,從而把之前存儲的數據一次拿出來計算,最后返回計算結果。
當然第一個例子也可以寫成第二個立即執行的這種形式,兩種方式可以互換。

### 總結
所謂的函數柯里化,亦或是在開發中設計到的其他一些概念。例如閉包、單例模式、觀察者模式等等都好,我們需要關注的點是在于掌握這些模式的設計思想,而不是去死記硬背,比如高大上的模式或者其他的,都是由普通js的技術來構成的,只是因為其設計思想而出現不同質的變化。