JavaScript函數(shù)式編程
函數(shù)式編程的基礎是一等函數(shù)(函數(shù)在js中作為一等公民)滋恬、作用域(詞法作用域,動態(tài)作用域)和閉包檬寂。
函數(shù)式編程的第一個概念是高等函數(shù):高等函數(shù)將函數(shù)作為參數(shù)宪潮,或者將函數(shù)作為返回值告匠。
高等函數(shù)是函數(shù)式編程的基礎,幾乎隨處可見高等函數(shù)著隆。
由函數(shù)構(gòu)建函數(shù)
首先討論函數(shù)的構(gòu)建扰楼。函數(shù)式編程通過高等函數(shù)和一等函數(shù)構(gòu)建,常用的方法有三種
柯里化
柯里化為每一個邏輯參數(shù)返回一個新函數(shù)美浦,分為手動柯里化和自動柯里化弦赖。
手動柯里化
自己手寫函數(shù)的柯里化版本,有柯里化方向的選擇(向左還是向右)
function div(n ,d) {
return n / d;
}
function leftCurry(n) {
return function (d) {
return n / d;
}
}
function rightCurry(d) {
return function (n) {
return n / d;
}
}
但這樣很麻煩浦辨,每個函數(shù)都要提供一個柯里化版本
自動柯里化
指我們通過一個curry
函數(shù)生成一個普通函數(shù)的柯里化版本
比如我們可以寫這樣一個curry2
函數(shù)做到手動柯里化中的那種效果
function curry2(func) {
return function (first) {
return function (second) {
return func(first, second);
};
};
}
function div(n, d) {
return n / d;
}
var leftCurry = curry(div);
這樣實現(xiàn)的一個leftCurry
跟手動柯里化實現(xiàn)的是一模一樣的蹬竖;但是自動柯里化是用過curry
函數(shù)和div
函數(shù)構(gòu)建出leftCurry
函數(shù)的;這樣做也限定了柯里化的方向;我們可以通過再編寫一個curry
函數(shù)使用另外一種方向來解決這個問題币厕。
再說一個自動柯里化的用處列另。這個柯里化如下:
function curry(func) {
return function (args) {
return func(args);
}
}
直觀上看這個柯里化有什么用?為什么不直接使用func(args)
呢旦装?
這個柯里化的場景在使用這樣的語句時格外有用[11, 11, 11, 11].map(parseInt)
時格外有用页衙。這行代碼貌似會返回[11, 11, 11, 11]
,但實際上的返回結(jié)果是[11, NaN, 3, 4]
阴绢。
這是為什么呢店乐?我們看了map
的源碼就能知道,map
接收的函數(shù)呻袭,實際上是一個iteratee(item, index, array)
响巢,也就是說,這行代碼實際運行的是[parseInt(11, 0, array) parseInt(11, 1, array), parseInt(11, 2, array), parseInt(11, 3, array)]
棒妨。
為了避免給這個iteratee
傳入過多的參數(shù),我們可以通過柯里化返回一個只接受一個參數(shù)的函數(shù)
[11, 11, 11, 11].parseInt(curry(parseInt))
含长,這樣我們就能得到理想的結(jié)果[11, 11, 11, 11]
.
柯里化的缺點
柯里化明顯只適合于有限參數(shù)的函數(shù)券腔;如果函數(shù)的參數(shù)過多(當然我們杜絕設計這樣的函數(shù))或者函數(shù)的參數(shù)未定,那就不適合用柯里化來構(gòu)建函數(shù)拘泞。這時就更適合用partial
纷纫。
partial
bind的實現(xiàn)其實就有partial
的寫法;因為bind
在傳遞上下文的時候陪腌,也是可以傳部分參數(shù)的辱魁。
Function.prototype.bind = function (context) {
context = Obejct(context) || window;
// 保存一部分參數(shù)
var args = [].slice.call(arguments, 1);
// ...其他操作
return function () {
// 補全參數(shù)
args = args.concat([].slice.call(arguments));
}
}
compose
compose
就是拼接函數(shù),返回一系列函數(shù)組合后的復合函數(shù)诗鸭,好比在數(shù)學里, 把函數(shù) f(), g(), 和 h() 組合起來可以得到復合函數(shù) f(g(h()))染簇。
遞歸
- 遞歸最有名的應用就是深克隆
- 遞歸操作都應該被封裝
函數(shù)式編程的組成
這里有兩個React
講到的概念:純函數(shù),不變性强岸。
純函數(shù)
什么是純函數(shù)锻弓?
- 返回結(jié)果只由參數(shù)決定,不受外部數(shù)據(jù)的影響
- 不改變外部狀態(tài)
- 不受
Math.random
蝌箍,Date.now
影響青灼,沒有this
,沒有全局變量
純函數(shù)便于函數(shù)的組成妓盲,有助于消除函數(shù)組合出來新的行為正確與否的擔憂:純函數(shù)的組合依舊是純函數(shù)杂拨。
不變性
js中的String
類是最適合說不變性的。字符串類型的變量在調(diào)用方法后悯衬,返回調(diào)用后的值弹沽,但字符串本身是沒有被改變的。
鏈式調(diào)用
鏈式調(diào)用看underscore
中_.chain
的源碼就好。