為什么要函數(shù)式編程?

用函數(shù)編程 != 函數(shù)式編程

幾乎所有編程語言中我們都會用到函數(shù),但大部分情況都不是函數(shù)編程辈讶。函數(shù)編程的重要特點之一是使用函數(shù)的聲明式推導來代替結構關鍵字的命令式過程。比如

const obj = {...}
const list = [...]

//命令式
for(let v of obj){...}
for(let k in obj){...}
for(let i=list.length;i--;){...}
while(...){...}

//聲明式
each(obj, (v,k)=>...)
each(list, (v,i)=>...)

這個例子很好的詮釋了兩種編程模式的區(qū)別 —— 聲明式只關注輸入/輸出蝎困,不在乎循環(huán)是for...of還是for...in又或者for還是while录语,相反,命令式則關注執(zhí)行細節(jié)禾乘,因為不同的語法決定了循環(huán)子是value還是key钦无。
那么,如果在編碼中用了each就是函數(shù)編程了盖袭?某種意義上是的失暂,但函數(shù)編程不僅僅是用each函數(shù)代替for關鍵字,它包含很多概念 —— 純函數(shù)鳄虱、高階函數(shù)弟塞、可推導性柯里化拙已、惰性計算等等决记,它是一種編程范式,是一種編碼習慣倍踪,更是一種思維模式系宫。關于什么是函數(shù)編程不是本文重點索昂,這里推薦一本很好的入門書 —— Luis Atencio的《JavaScript函數(shù)式編程指南》。

為什么要用函數(shù)式編程扩借?

難道命令式不能實現(xiàn)功能嗎椒惨?函數(shù)隱藏了細節(jié),錯了怎么辦潮罪?用了那么多函數(shù)還不叫函數(shù)式編程康谆?在具體舉例描述函數(shù)式編程的優(yōu)點前,我們先來回顧一下軟件的一些常見定義 —— 可讀性嫉到、健壯性沃暗、可維護性、運行效率何恶、封裝度孽锥。
下面通過一些編碼中常見的例子來對比函數(shù)式與命令式的區(qū)別:

場景1 —— 可讀性

  1. 當我們要判斷一個集合是否為空
//命令式
if(list.length < 1){
  ...
}
//聲明式
if(isEmpty(list)){
  ...
}

顯然從可讀性來看聲明式更好,況且命令式為了確保代碼運行正常還需要驗證list變量本身必須是Array類型细层,否則獲取.length屬性將收到一個錯誤惜辑,它看起來就像這樣

//命令式
if(list != null && list.length < 1){
  ...
}
  1. 輸入框字段不能為空
//命令式
if(name != null && name != undefined && name != ''){
  ...
}
//聲明式
if(!isBlank(name)){
  ...
}

例子中的函數(shù)不僅在可讀性上更勝一籌,同時也解決了代碼容錯問題今艺,我們不關心入?yún)⑹欠穹虾瘮?shù)內(nèi)部邏輯的邏輯韵丑,是否是一個有效值爵卒,也不用擔心無效值會導致程序異常虚缎,這是函數(shù)式編程中的一個概念 —— 透明引用 —— 如果 f(x)=y 那么 f(!x)=!y。

場景2 —— 健壯性

健壯性是函數(shù)式編程最重要的優(yōu)勢钓株,可以減少大量的潛在錯誤实牡,包括無效引用、類型異常轴合、范圍溢出等等创坞。

  1. 容錯處理
//不健壯
if(a.b == null){...}
list.forEach(...)
ary.splice(...)
obj['key']()
let x = list[5]

//健壯
if(isNull(get(a,'b'))){...}
each(list,...)
splice(ary,...)
get(obj,'key',()=>{})()

通過函數(shù)組合及默認值設置可以避免這些潛在錯誤。

  1. 錯誤調(diào)試Debug
    恭喜受葛,函數(shù)式編程中針對函數(shù)本身不存在調(diào)試問題题涨,也就是說函數(shù)庫提供的函數(shù)無需調(diào)試。函數(shù)式編程所依賴的函數(shù)庫中的函數(shù)在使用時可以看作宿主系統(tǒng)提供的原生函數(shù)总滩,例如c語言中的printf纲堵,js中的console.log,java中的Math.random闰渔,python中的print等等席函。原因是函數(shù)式編程中提供的函數(shù)大部分都是純函數(shù),純函數(shù)的特征就是確定的輸入一定有確定的輸出冈涧,具有明確的可推導性茂附。
    下面這個函數(shù)isNumber當參數(shù)類型已知時正蛙,返回值可推導而不會產(chǎn)生程序異常或任何非boolean類型的結果营曼。
//返回值一定是boolean類型
const rs = isNumber(null) //true | false

當然乒验,在函數(shù)鏈模式下數(shù)據(jù)流本身是需要跟蹤調(diào)試的,比如

_([1,2,3,4])
.map(v=>v*3)
.tap(v=>console.log(v))//調(diào)試數(shù)據(jù)流
.join('-')
.value()

場景3 —— 可維護性

當我們開始維護代碼邏輯時溶推,最重要的當然是能看得通徊件,理得順。除了可讀性要高之外蒜危,邏輯間的可推導性也非常重要虱痕。比如,每個函數(shù)的返回值就是看上去應該返回的那個值辐赞。

  1. 字符串首字母大寫
//自定義
function cap(str){
  //獲取str首字母及后續(xù)字符串
  //把首字母變大寫
  //返回大寫后的首字母 + 后續(xù)字符串
}
const str = cap('abc') //Abc | Error | ...

//使用函數(shù)庫
const str = capitalize('abc') //Abc

這是個很簡單的邏輯實現(xiàn)部翘,但區(qū)別是capitalize函數(shù)的返回值一定是字符串,而cap函數(shù)的返回值有以下幾種可能

  • 正確字符串
  • 異常字符串
  • 錯誤中斷
    比如响委,如果參數(shù)是null新思,就無法獲取首字母及變大寫,這些操作最終會導致Error而中斷赘风,這就是不可推導性夹囚。整個邏輯會因為某些函數(shù)的不可預測而產(chǎn)生不穩(wěn)定性,但對于capitalize函數(shù)邀窃,它的返回值永遠可預測荸哟。
  1. 獲取集合大小
//命令式
Object.keys(obj)
ary.length
set.size
//函數(shù)式
size(obj)
size(ary)
size(set)

統(tǒng)一的API有助于快速理解代碼意圖,良好的函數(shù)封裝可以屏蔽不同原生數(shù)據(jù)集合間的差異瞬捕,獲取size則是集合最常用的操作之一鞍历。

場景4 —— 封裝、重用

封裝是函數(shù)的基本特性肪虎,我們在編碼時創(chuàng)建自定義函數(shù)也是為了進行過程封裝及重用劣砍。函數(shù)式編程通常都會依賴于一個強大且豐富的函數(shù)庫(函數(shù)語言內(nèi)置函數(shù)庫,而js可以通過使用外置函數(shù)庫)且在不同的方面提供了多種封裝(比如本文開篇介紹的each函數(shù))扇救。

  1. 獲取集合最后一個元素
//未封裝
const lastOne = list[list.length-1]
//已封裝
const lastOne = last(list)
  1. 集合映射 1 ? 1
//未封裝
const newList = [];
list.forEach(v=>newList.push(v+1))
//已封裝
const newList = map(list,v=>v+1)
  1. 集合映射 m ? n
//未封裝
const newList = [];
list.forEach(v=>if(n%2)newList.push(v+1))
//已封裝
const newList = flatMap(list,n=>n%2?n:[])
  1. 數(shù)據(jù)分組
const users = [
 {name:'zhangsan',sex:'m',age:33},
 {name:'lisi',sex:'f',age:21},
 {name:'wangwu',sex:'m',age:25},
 {name:'zhaoliu',sex:'m',age:44},
]
//未封裝
const grouped = {}
each(users, u=>{
  if(!grouped[u.sex])grouped[u.sex] = []
  grouped[u.sex].push(u)
})
//已封裝
const grouped = groupBy(users, u=>u.sex)

更多封裝的js函數(shù)庫可以查看這里;

場景5 —— 性能

針對js來說刑枝,如果做web開發(fā)數(shù)據(jù)量一大就會導致UI卡頓而影響使用體驗,尤其是大數(shù)據(jù)量的內(nèi)容處理比如嵌套循環(huán)或者大量數(shù)據(jù)進行分組/映射時可能會出現(xiàn)性能瓶頸迅腔。在函數(shù)式編程中可通過惰性計算列表推導來解決大數(shù)據(jù)量集合處理装畅。
關于惰性計算及列表推導可以查看這里;關于集合操作性能優(yōu)化可以查看這里

結語

函數(shù)式編程已經(jīng)越來越多的影響到各種語言,即使是OOP大佬的JAVA也引入了stream API來處理集合數(shù)據(jù)钾挟,而對家C#也是更早引入了lambda洁灵。函數(shù)式編程并不是要放棄OOP,他們的關注點不同(但明顯js的函數(shù)性更純一些)。
學習并習慣函數(shù)式編程是一個漸進的過程徽千,不妨從上面列舉的幾個場景開始苫费,逐步了解掌握函數(shù)式編程。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末双抽,一起剝皮案震驚了整個濱河市百框,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌牍汹,老刑警劉巖铐维,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異慎菲,居然都是意外死亡嫁蛇,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門露该,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睬棚,“玉大人,你說我怎么就攤上這事解幼∫值常” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵撵摆,是天一觀的道長底靠。 經(jīng)常有香客問我,道長特铝,這世上最難降的妖魔是什么暑中? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮苟呐,結果婚禮上痒芝,老公的妹妹穿的比我還像新娘俐筋。我一直安慰自己牵素,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布澄者。 她就那樣靜靜地躺著笆呆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粱挡。 梳的紋絲不亂的頭發(fā)上赠幕,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音询筏,去河邊找鬼榕堰。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的逆屡。 我是一名探鬼主播圾旨,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼魏蔗!你這毒婦竟也來了砍的?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤莺治,失蹤者是張志新(化名)和其女友劉穎廓鞠,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谣旁,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡床佳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了榄审。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夕土。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瘟判,靈堂內(nèi)的尸體忽然破棺而出怨绣,到底是詐尸還是另有隱情,我是刑警寧澤拷获,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布篮撑,位于F島的核電站,受9級特大地震影響匆瓜,放射性物質(zhì)發(fā)生泄漏赢笨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一驮吱、第九天 我趴在偏房一處隱蔽的房頂上張望茧妒。 院中可真熱鬧,春花似錦左冬、人聲如沸桐筏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梅忌。三九已至,卻和暖如春除破,著一層夾襖步出監(jiān)牢的瞬間牧氮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工瑰枫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留踱葛,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像尸诽,于是被迫代替她去往敵國和親圾笨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容