柯理化函數(shù)和組合函數(shù)都?xì)w屬于函數(shù)式編程蟀瞧,用于解決函數(shù)式編程的問題
首先先說明一下函數(shù)式編程和面向?qū)ο笫骄幊痰膬?yōu)缺點
* 面向?qū)ο缶幊痰? 優(yōu)點
程序更加便于分析怠噪、設(shè)計、理解
是易拓展的获洲,由于繼承、封裝塘秦、多態(tài)的特性定踱,
自然設(shè)計出高內(nèi)聚筷狼、低耦合的系統(tǒng)結(jié)構(gòu)瓶籽,使得系統(tǒng)更靈活、更容易擴展埂材,而且成本較低
缺點
為了寫可重用的代碼而產(chǎn)生了很多無用的代碼塑顺,導(dǎo)致代碼膨脹
* 函數(shù)式編程的
優(yōu)點
代碼可讀性更強。
實現(xiàn)同樣的功能函數(shù)式編程所需要的代碼比面向?qū)ο缶幊桃俸芏啵? 代碼更加簡潔明晰
開發(fā)速度快
缺點
所有的變量在程序運行期間都是一直存在的俏险,非常占用運行資源
柯理化函數(shù)currying
- 概念:是把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù)严拒,并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。
示例:
//普通函數(shù)
function add(n1, n2) {
return n1 + n2
}
add(1, 2)
//柯理化函數(shù)
function add(n1) {//把參數(shù)一個一個的傳入
return function (n2) {//返回帶著返回結(jié)果的新函數(shù)
return n1 + n2
}
}
add(1)(2);
- 為什么使用柯理化函數(shù)
柯理化就是利用模塊化思想處理多參函數(shù)竖独,通過組合函數(shù)減少每個函數(shù)的入?yún)?shù)量裤唠,從而提高代碼的可閱讀性及可維護性。
創(chuàng)建一個柯理化函數(shù)
function currying(fun) {
var args = Array.prototype.slice.call(arguments, 1);
return function () {
var _args = args.concat(Array.prototype.slice.call(arguments));
return fun.apply(null, _args);
}
}
利用柯理化函數(shù)改造一個函數(shù)
function add(...vals){
return vals.reduce((pre, val) = > {
return pre + val;
});
}
var newAdd = currying(add, 1, 2, 3);
console.log(newAdd(4, 5, 6)); // 21
但是上邊封裝的柯理化函數(shù)的代碼莹痢,只能使用一次种蘸,不能夠多次執(zhí)行
function currying(fn) {
var args = [].slice.call(arguments, 1);
return function () {
if (arguments.length == 0) {
return fn.apply(null, args);//如果不帶參數(shù)調(diào)用,就說明參數(shù)已經(jīng)傳完了竞膳,要返回結(jié)果了
} else {
args = args.concat([].slice.call(arguments));//如果調(diào)用時帶著參數(shù)航瞭,只需要把參數(shù)添加進去,利用不銷毀作用域坦辟,下次再調(diào)用的時候就包含上次傳遞的參數(shù)了
}
}
}
多次調(diào)用
function add() {
var vals = Array.prototype.slice.call(arguments);
return vals.reduce((pre, val) = > {
return pre + val;
});
}
var newAdd = currying(add, 1, 2, 3);
newAdd(4, 5);
newAdd(6, 7);
console.log(newAdd()); // 28
//把每次函數(shù)調(diào)用的參數(shù)都存儲起來刊侯,如果已無參形式調(diào)用,說明記錄結(jié)束锉走,需要做最終計算滨彻。
高級柯理化函數(shù)
function sum(a, b, c) {
return a + b + c;
}
sum(1,2,3)
//想實現(xiàn)一個柯理化函數(shù)
let curried= curry(sum);
curried(1,2,3) //6
curried(1)(2,3) //6
curried(1)(2)(3) //6
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
//如果傳入的參數(shù)個數(shù)的和一直小于原函數(shù)的個數(shù)就遞歸執(zhí)行,直到參數(shù)全部執(zhí)行完就調(diào)用原函數(shù)
偏函數(shù)和柯理化函數(shù)的概念及區(qū)別
- 當(dāng)把已知函數(shù)的一些參數(shù)固定挪蹭,結(jié)果函數(shù)被稱為偏函數(shù)亭饵,通過使用bind獲得偏函數(shù),也有其他方式實現(xiàn)嚣潜。
eg: 當(dāng)我們不想一次一次重復(fù)相同的參數(shù)時冬骚,偏函數(shù)是很便捷的。如我們有send(from,to)函數(shù)懂算,如果from總是相同的只冻,可以使用偏函數(shù)簡化調(diào)用。 - 柯里化是轉(zhuǎn)換函數(shù)調(diào)用從f(a,b,c)至f(a)(b)(c).Javascript通常既實現(xiàn)正常調(diào)用计技,也實現(xiàn)參數(shù)數(shù)量不足時的偏函數(shù)方式調(diào)用喜德。
組合函數(shù)compose
簡單來說,就是把很多函數(shù)組合到一起垮媒,執(zhí)行一個函數(shù)舍悯,所有的函數(shù)都執(zhí)行了
- 將函數(shù)串聯(lián)起來執(zhí)行航棱,將多個函數(shù)組合起來,一個函數(shù)的輸出結(jié)果是另一個函數(shù)的輸入?yún)?shù)萌衬,一旦第一個函數(shù)開始執(zhí)行饮醇,就會像多米諾骨牌一樣推導(dǎo)執(zhí)行了。
- 特點
compose的參數(shù)是函數(shù)秕豫,返回的也是一個函數(shù)
因為除了第一個函數(shù)的接受參數(shù)朴艰,其他函數(shù)的接受參數(shù)都是上一個函數(shù)的返回值
compsoe函數(shù)可以接受任意的參數(shù),所有的參數(shù)都是函數(shù)混移,且執(zhí)行方向是自右向左的祠墅,初始函數(shù)一定放到參數(shù)的最右面
示例
var greeting = (firstName, lastName) =>'hello, ' + firstName + ' ' + lastName + ' ';
var toUpper = str =>str.toUpperCase();
var fn = compose(toUpper, greeting);//從右向左執(zhí)行
//只有g(shù)reeting接受參數(shù)'jack', 'smith',toUpper 接受的參數(shù)是greeting的返回值
console.log(fn('jack', 'smith'))// ‘HELLO歌径,JACK SMITH’
我使用的reduce實現(xiàn)的compose
function compose(){
let args=[].slice.call(arguments);
var length = args.length;
var index = length;
while (index--) {//如果參數(shù)中存在不是函數(shù)類型的值毁嗦,就拋出錯誤
if (typeof args[index] !== 'function') {
throw new TypeError('Expected a function');
}
}
let last=args.pop();
args=args.reverse();
return function (){
let _args = [].slice.call(arguments);
return args.reduce((prev, next)=>{
return next(prev);//每一個函數(shù)執(zhí)行的返回值都作為上一個函數(shù)的參數(shù)傳進去
},last.apply(null,_args))//最后一個函數(shù)是需要傳值的
}
}
繼續(xù)執(zhí)行
var trim = str =>str.replace(/\s+/g, '|');
var newFn = compose(trim, fn);
console.log(newFn('jack', 'smith'));
- loadsh的flow / flowRight實現(xiàn) (利用while循環(huán))
flow 返回一個函數(shù),連續(xù)調(diào)用參數(shù)中傳遞的函數(shù)數(shù)組
//lodash的實現(xiàn) 從左向右執(zhí)行的函數(shù)
var flow = function (funcs) {
var length = funcs.length;
var index = length
while (index--) {
if (typeof funcs[index] !== 'function') {
throw new TypeError('Expected a function');
}
}
return function (...args){
var index = 0
var result = length ? funcs[index].apply(this, args) : args[0]
while (++index < length) {
result = funcs[index].call(this, result)
}
return result
}
}
var flowRight = function (funcs) {
return flow(funcs.reverse())
}