JavaScript中的函數(shù)式編程詳解
函數(shù)式編程
函數(shù)式編程是一種編程范式,是一種構(gòu)建計(jì)算機(jī)程序結(jié)構(gòu)和元素的風(fēng)格,它把計(jì)算看作是對(duì)數(shù)學(xué)函數(shù)的評(píng)估,避免了狀態(tài)的變化和數(shù)據(jù)的可變,與函數(shù)式編程相對(duì)的是命令式編程。我們有這樣一個(gè)需求,給數(shù)組的每個(gè)數(shù)字加一:
// 數(shù)組每個(gè)數(shù)字加一, 命令式編程 let arr = [1, 2, 3, 4]; let newArr = []; for(let i = 0; i < arr.length; i++){ newArr.push(arr[i] + 1); } console.log(newArr); // [2, 3, 4, 5]
這段代碼結(jié)果沒有問題,但是沒法重用。我們換一個(gè)思維,這里面包含的操作其實(shí)就兩個(gè),一個(gè)是遍歷數(shù)組,一個(gè)是成員加一。我們把這兩個(gè)方法拆出來:
// 先拆加一出來 let add1 = x => x +1; // 然后拆遍歷方法出來,通過遍歷返回一個(gè)操作后的新數(shù)組 // fn是我們需要對(duì)每個(gè)數(shù)組想進(jìn)行的操作 let createArr = (arr, fn) => { const newArr = []; for(let i = 0; i < arr.length; i++){ newArr.push(fn(arr[i])); } return newArr; } // 用這兩個(gè)方法來得到我們期望的結(jié)果 const arr = [1, 2, 3, 4]; const newArr = createArr(arr, add1); console.log(newArr); // [2, 3, 4, 5], 結(jié)果仍然是對(duì)的
這樣拆分后,如果我們下次的需求是對(duì)數(shù)組每個(gè)元素乘以2,我們只需要寫一個(gè)乘法的方法,然后復(fù)用之前的代碼就行:
let multiply2 = x => x * 2; // 調(diào)用之前的createArr const arr2 = [1, 2, 3, 4]; const newArr2 = createArr(arr2, multiply2); console.log(newArr2); // [2, 4, 6, 8], 結(jié)果是對(duì)的
事實(shí)上我們的加一函數(shù)只能加一,也不好復(fù)用,它還可以繼續(xù)拆:
// 先寫一個(gè)通用加法,他接收第一個(gè)加數(shù),返回一個(gè)方法 // 返回的這個(gè)方法接收第二個(gè)加數(shù),第一個(gè)加數(shù)是上層方法的a // 這樣當(dāng)我們需要計(jì)算1+2是,就是add(1)(2) let add = (a) => { return (b) => { return a + b; } } // 我們也可以將返回的函數(shù)賦給一個(gè)變量,這個(gè)變量也就變成一個(gè)能特定加a的一個(gè)方法 let add1 = add(1); let res = add1(4); console.log(res); // 5
所以函數(shù)式編程就是將程序分解為一些更可重用、更可靠且更易于理解的部分,然后將他們組合起來,形成一個(gè)更易推理的程序整體。
純函數(shù)
純函數(shù)是指一個(gè)函數(shù),如果它的調(diào)用參數(shù)相同,則永遠(yuǎn)返回相同的結(jié)果。它不依賴于程序執(zhí)行期間函數(shù)外部任何狀態(tài)或數(shù)據(jù)的變化,只依賴于其輸入?yún)?shù)。同時(shí)函數(shù)的運(yùn)行也不改變?nèi)魏瓮獠繑?shù)據(jù),它只通過它的返回值與外部通訊。下面這個(gè)函數(shù)就不是純函數(shù),因?yàn)楹瘮?shù)內(nèi)部需要的discount需要從外部獲?。?/p>
let discount = 0.8; const calPrice = price => price * discount; let price = calPrice(200); // 160 // 當(dāng)discount變了,calPrice傳同樣額參數(shù),結(jié)果不一樣,所以不純 discount = 0.9; price = calPrice(200); // 180
要改為純函數(shù)也很簡(jiǎn)單,將discount作為參數(shù)傳遞進(jìn)去就行了
const calPrice = (price, discount) => price * discount;
純函數(shù)可以保證代碼的穩(wěn)定性,因?yàn)橄嗤妮斎胗肋h(yuǎn)會(huì)得到相同結(jié)果。不純的函數(shù)可能會(huì)帶來副作用。
函數(shù)副作用
函數(shù)副作用是指調(diào)用函數(shù)時(shí)除了返回函數(shù)值之外,還對(duì)主調(diào)用函數(shù)產(chǎn)生附加的影響,比如修改全局變量或者外部變量,或者修改參數(shù)。這可能會(huì)帶來難以查找的問題并降低代碼的可讀性。下面的foo就有副作用,當(dāng)后面有其他地方需要使用a,可能就會(huì)拿到一個(gè)被污染的值
let a = 5; let foo = () => a = a * 10; foo(); console.log(a); // 50
除了我們自己寫的函數(shù)有副作用外,一些原生API也可能有副作用,我們寫代碼時(shí)應(yīng)該注意:
我們的目標(biāo)是盡可能的減少副作用,將函數(shù)寫為純函數(shù),下面這個(gè)不純的函數(shù)使用了new Date,每次運(yùn)行結(jié)果不一樣,是不純的:
要給為純函數(shù)可以將依賴注入進(jìn)去,所謂依賴注入就是將不純的部分提取出來作為參數(shù),這樣我們可以讓副作用代碼集中在外部,遠(yuǎn)離核心代碼,保證核心代碼的穩(wěn)定性
// 依賴注入 const foo = (d, log, something) => { const dt = d.toISOString(); return log(`${dt}: ${something}`); } const something = 'log content'; const d = new Date(); const log = console.log.bind(console); foo(d, log, something);
所以減少副作用一般的方法就是:
1. 函數(shù)使用參數(shù)進(jìn)行運(yùn)算,不要修改參數(shù)
2. 函數(shù)內(nèi)部不修改外部變量
3. 運(yùn)算結(jié)果通過返回值返回給外部
可變性和不可變性
- 可變性:指一個(gè)變量創(chuàng)建以后可以任意修改
- 不可變性: 指一個(gè)變量被創(chuàng)建后永遠(yuǎn)不會(huì)發(fā)生改變,不可變性是函數(shù)式編程的核心概念
下面是一個(gè)可變的例子:
如果我們一定要修改這個(gè)參數(shù),我們應(yīng)該將這個(gè)參數(shù)進(jìn)行深拷貝后再操作,這樣就不會(huì)修改參數(shù)了:
到此這篇關(guān)于JavaScript中的函數(shù)式編程詳解的文章就介紹到這了,更多相關(guān)js函數(shù)式編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
原生js實(shí)現(xiàn)一個(gè)放大鏡效果超詳細(xì)
這篇文章主要介紹了原生js實(shí)現(xiàn)一個(gè)放大鏡效果超詳細(xì),文章圍繞主題展開詳細(xì)的內(nèi)容,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09JavaScript中高級(jí)語法??表達(dá)式用法示例詳解
這篇文章主要為大家介紹了JavaScript中高級(jí)語法??表達(dá)式用法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04EasyUi中的Combogrid 實(shí)現(xiàn)分頁和動(dòng)態(tài)搜索遠(yuǎn)程數(shù)據(jù)
jquery easyui中的combogrid比較特殊,算是combo和grid的組合,combogrid結(jié)合一個(gè)可編輯的文本框和下拉數(shù)據(jù)網(wǎng)格面板,可以讓用戶迅速找到并選擇,又可以進(jìn)行搜索,展示與當(dāng)前輸入的字符相匹配的數(shù)據(jù)。下面給大家介紹EasyUi中的Combogrid 實(shí)現(xiàn)分頁和動(dòng)態(tài)搜索遠(yuǎn)程數(shù)據(jù)2016-04-04js實(shí)現(xiàn)各種復(fù)制到剪貼板的方法(分享)
下面小編就為大家?guī)硪黄猨s實(shí)現(xiàn)各種復(fù)制到剪貼板的方法(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10JavaScript深度復(fù)制(deep clone)的實(shí)現(xiàn)方法
本文給大家介紹JavaScript深度復(fù)制(deep clone)的實(shí)現(xiàn)方法,涉及到j(luò)s深度復(fù)制相關(guān)知識(shí),本文介紹的非常詳細(xì),特此分享腳本之家平臺(tái)供大家參考2016-02-02JS網(wǎng)頁在線獲取鼠標(biāo)坐標(biāo)值的方法
這篇文章主要介紹了JS網(wǎng)頁在線獲取鼠標(biāo)坐標(biāo)值的方法,涉及javascript操作頁面窗口位置元素的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02原生js+canvas實(shí)現(xiàn)驗(yàn)證碼
這篇文章主要為大家詳細(xì)介紹了原生js+canvas實(shí)現(xiàn)驗(yàn)證碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11js復(fù)制內(nèi)容到剪貼板代碼,js復(fù)制代碼的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄猨s復(fù)制內(nèi)容到剪貼板代碼,js復(fù)制代碼的簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10