參考書(shū)籍:《javascript 函數(shù)式編程》
什么是函數(shù)式編程户辱?
函數(shù)式編程通過(guò)函數(shù)將值轉(zhuǎn)換為抽象單元透揣,接著用于構(gòu)建軟件系統(tǒng)俺抽。
函數(shù)式編程技術(shù)有什么?
- 確定抽象沸伏,并為其構(gòu)建函數(shù)
- 利用已有的函數(shù)來(lái)構(gòu)建更為復(fù)雜的抽象
- 通過(guò)將現(xiàn)有的函數(shù)傳給其他的函數(shù)來(lái)構(gòu)建更加復(fù)雜的抽象
為什么函數(shù)式編程很重要糕珊?
首先先從面向?qū)ο蟮慕嵌葋?lái)講,我們主要目標(biāo)會(huì)將問(wèn)題分解毅糟。如圖所示红选。
把這些對(duì)象聚集起來(lái),組合成更大的部件
從圖中不難看出姆另,里面有很多重復(fù)的函數(shù)纠脾,因?yàn)槊嫦驅(qū)ο螅詴?huì)著重于解決每個(gè)組件里的需求蜕青。
相比較而言苟蹈,嚴(yán)格的函數(shù)式編程也會(huì)將一個(gè)問(wèn)題分為幾部分函數(shù)來(lái)解決。
與面向?qū)ο缶幊填?lèi)似右核,函數(shù)式編程也通過(guò)“黏結(jié)”或“組合”其他函數(shù)的方式來(lái)構(gòu)建更大的函數(shù)慧脱,實(shí)現(xiàn)更加抽象的行為。
在一個(gè)面向?qū)ο笙到y(tǒng)的內(nèi)部贺喝,我們會(huì)發(fā)現(xiàn)對(duì)象之間的交互會(huì)引起各個(gè)對(duì)象內(nèi)部狀態(tài)的變化菱鸥,而整個(gè)系統(tǒng)的變化就是由這些狀態(tài)變化混合來(lái)形成的。
相比之下躏鱼,函數(shù)式編程系統(tǒng)則努力減少可見(jiàn)的狀態(tài)修改氮采。
函數(shù)式編程以命令的方式構(gòu)建系統(tǒng),并通過(guò)顯性的狀態(tài)改變縮減到最小來(lái)變得更加模塊化染苛。實(shí)踐中的函數(shù)式編程不是以消除狀態(tài)改變?yōu)槟康娜的菍⒁阎到y(tǒng)中突變盡量縮小到最小區(qū)域中
以函數(shù)為抽象單元
抽象方法是指隱藏了實(shí)現(xiàn)細(xì)節(jié)的函數(shù)。
//未隱藏細(xì)節(jié)的函數(shù)
function parseAge(age) {
if (!_.isString(age)) throw new Error('Expecting a string');
var a;
console.log("Attempting to parse an age");
a = parseInt(age, 10);
if(_.isNaN(a)) {
console.log(["Could not parse age:", age].join(' '));
a = 0;
}
return a;
}
//隱藏細(xì)節(jié)
function fail(thing) {
throw new Error(thing);
}
function warn(thing) {
console.log(["WARNING", thing].join(' '));
}
function note(thing) {
console.log(["NOTE", thing].join(' '));
}
function parseAge(age) {
if(!_.isString(age)) fail("Expecting a string");
var a;
note("Attempt to parse an age");
a = parseInt(age, 10);
if(_.isNaN(a)) {
warn(["Could not parse age:", age].join(' '));
a = 0;
}
return a;
}
對(duì)比上述代碼茶行,其實(shí)現(xiàn)的功能其實(shí)是一樣的躯概,不同的是現(xiàn)在報(bào)告錯(cuò)誤、信息和警告的想法已經(jīng)被抽象化了畔师。這樣在修改輸出錯(cuò)誤娶靡、信息和警告呈現(xiàn)的方式,就不用修改相應(yīng)的代碼行看锉,以及其他地方的類(lèi)似輸出模式姿锭,而是直接添加錯(cuò)誤函數(shù)的參數(shù)塔鳍。
封裝和隱藏
在面向?qū)ο笾校庋b是指一種將若干個(gè)數(shù)據(jù)與用來(lái)操縱它們的特定操作包裝起來(lái)的方式呻此。比如說(shuō)新建一個(gè)對(duì)象轮纫,就可以包含一個(gè)數(shù)組及操縱這個(gè)數(shù)組的push、pop方法趾诗,這就是封裝蜡感。
然而,有時(shí)在限制元素的可見(jiàn)性時(shí)也會(huì)用到封裝恃泪,這個(gè)時(shí)候就稱(chēng)為數(shù)據(jù)隱藏郑兴,在js中就使用閉包來(lái)隱藏?cái)?shù)據(jù)(閉包有好有壞,使用需慎重贝乎,否則很容易把自己搞暈情连,并且可能會(huì)使代碼可讀性變差。)
以函數(shù)為行為單元
就是用函數(shù)來(lái)進(jìn)行簡(jiǎn)單的存儲(chǔ)和傳遞基本行為览效,函數(shù)命名通常會(huì)讓人一目了然知道該函數(shù)得到的結(jié)果却舀,發(fā)出的行為。比如sort函數(shù)锤灿,就會(huì)知道是分類(lèi)的函數(shù)挽拔。
數(shù)據(jù)抽象
函數(shù)式編程旨在用最簡(jiǎn)單的數(shù)據(jù)來(lái)實(shí)現(xiàn)高層級(jí)的行為。在代入簡(jiǎn)單數(shù)據(jù)后最終實(shí)現(xiàn)復(fù)雜的行為 但校。
我們會(huì)發(fā)現(xiàn)螃诅,在處理與人有關(guān)的數(shù)據(jù)時(shí)更適合用函數(shù)式編程的方式,而面向?qū)ο蟮姆椒ǜm合與模擬人状囱。
函數(shù)式編程速度术裸。。
在函數(shù)式編程中經(jīng)常會(huì)把各種行為抽象出來(lái)成為單獨(dú)的函數(shù)亭枷,然后再組合起來(lái)執(zhí)行袭艺。這樣在我們之前講到的Event loop線(xiàn)程中我們會(huì)感動(dòng)困惑,因?yàn)樵趈avascript中那樣事件一次一次掛載起來(lái)執(zhí)行的話(huà)速度可能會(huì)很慢叨粘。
嗯猾编,是的,應(yīng)該是會(huì)這樣的宣鄙。因此袍镀,在一些把速度作為第一要領(lǐng)比如游戲界面的網(wǎng)站我們會(huì)更傾向于使用面向?qū)ο缶幊?/strong>。
然而在一些復(fù)雜度較高冻晤,項(xiàng)目較為龐大的時(shí)候,使用函數(shù)式編程則會(huì)讓我們的代碼可讀性增強(qiáng)绸吸,而且處理數(shù)據(jù)的時(shí)候出錯(cuò)的可能性更低瞳腌。