純函數(shù)是什么?怎么合理運用純函數(shù)?
作者:xmanlin
來源:Segmentfault 思否社區(qū)
前言
純函數(shù)這個這個詞我相信小伙伴們多多少少都聽說過,它是函數(shù)式編程的基礎(chǔ)。本文主要是對純函數(shù)進(jìn)行探討,包括基本概念,優(yōu)點,運用的經(jīng)典案例以及我們?nèi)粘T撊绾稳ズ侠淼氖褂玫鹊取?/span>
純函數(shù)的概念
首先我們來看看純函數(shù)的基本概念:
相同的輸入,總是會的到相同的輸出,并且在執(zhí)行過程中沒有任何副作用。
該怎么去理解上面的概念呢?我們要把上面這句話拆成兩部分來看。
相同的輸入,總是會得到相同的輸出。
來看看下面的例子:
let a = 1;
function xAdd(x) {
return x + a;
};
xAdd(1); //2
上面這個函數(shù)就不是一個純函數(shù),因為在我們程序執(zhí)行的過程中,變量a很可能會發(fā)生改變,當(dāng)變量a發(fā)生改變時,我們同樣執(zhí)行xAdd(1)時得到的輸出也就不同了。
再看另一個例子:
function sum(x, y) {
return x + y;
};
sum(1,2); //3
在這個例子中,符合相同的輸入得到相同的輸出這個概念,sum是一個純函數(shù)。
執(zhí)行過程中沒有任何副作用。
這里我們要搞清楚什么是副作用,這里的副作用指的是函數(shù)在執(zhí)行過程中產(chǎn)生了外部可觀察變化。
發(fā)起HTTP請求 操作DOM 修改外部數(shù)據(jù) console.log()打印數(shù)據(jù) 調(diào)用Date.now()或者M(jìn)ath.random()
上面一系列操作都可以被稱為是副作用。下面可以接著看一個修改外部數(shù)據(jù)從而產(chǎn)生副作用的例子:
let a = 1;
function func() {
a = 'b';
};
func();
console.log(a); // b
我們運行了func函數(shù),外部的變量a的值發(fā)生了改變,這就是產(chǎn)生了所謂的副作用,所以func不是一個純函數(shù)。當(dāng)我們這樣進(jìn)行修改:
function func2() {
let a = 1;
a = 'a';
return a
};
func(); // a
函數(shù)fun2不會對產(chǎn)生外部可觀察變化,也就不會產(chǎn)生副作用,它就是一個純函數(shù)。
一個純函數(shù),上面所說的兩個條件缺一不可。
純函數(shù)的好處
通過了解純函數(shù)的概念,我相信有的小伙伴已經(jīng)能感覺到純函數(shù)的一些的好處了:
更容易進(jìn)行測試,結(jié)果只依賴輸入,測試時可以確保輸出穩(wěn)定 更容易維護(hù)和重構(gòu),我們可以寫出質(zhì)量更高的代碼 更容易調(diào)用,我們不用擔(dān)心函數(shù)會有什么副作用 結(jié)果可以緩存,因為相同的輸入總是會得到相同的輸出
純函數(shù)運用的經(jīng)典案例
既然純函數(shù)有這么多好處,那么我們來看看有哪些運用純函數(shù)的經(jīng)典案例。
數(shù)組的基本方法
數(shù)組的很多基本方法都是純函數(shù),例如map,forEach,filter,reduce等等。
redux中的reducer
Redux中三大原則之一使用純函數(shù)來執(zhí)行修改,其中就運用了Reducer來描述 action 如何改變 state tree。
Reducer 只是一些純函數(shù),它接收先前的 state 和 action,并返回新的 state。--Redux
中文文檔
Lodash
Lodash 是一個一致性、模塊化、高性能的 JavaScript 實用工具庫。我相信很多小伙伴也經(jīng)常用到吧,這也是純函數(shù)代表。
當(dāng)然還有很多,這里就不一一舉例了,總的來說,純函數(shù)還是十分常見的。
我們?nèi)绾魏侠砣ナ褂?/span>
在實際開發(fā)中,我們可以合理的去運用純函數(shù)來提高我們的開發(fā)效率和代碼質(zhì)量。
純函數(shù)組件
我們可以使用純函數(shù)的的方式來創(chuàng)建組件:
function Header(props) {
return <h2>{props.text}</h2>
}
對比一下使用Class(類)組件的方式創(chuàng)建組件:
class Header extends React.Component {
render() {
return <h1>{this.props.text}</h1>
}
}
我們可以總結(jié)出純函數(shù)組件的一些優(yōu)點:
無副作用,我們不用擔(dān)心副作用帶來的一些難以捕捉的問題 語法更簡潔,可讀性好,代碼量相對較小,易復(fù)用 占用內(nèi)存小,無生命周期和狀態(tài)管理,提升了性能
當(dāng)然純函數(shù)組件也有自己的缺點,例如:沒有生命周期。
生命周期有時候并不可少,所幸現(xiàn)在我們也已經(jīng)有了很好的解決方案——react-hooks。利用hooks函數(shù),我們可以在函數(shù)組件中使用等價于生命周期,狀態(tài)管理等方法。
合理運用純函數(shù)編寫公共方法
在編寫公共方法的時候,我們盡量用純函數(shù)來進(jìn)行編寫。
假設(shè)我們要編寫一個把數(shù)組中的小寫字母轉(zhuǎn)為大寫字母的公共方法:
let lists = ["q","w","e"];
let upperCaseLists = () => {
let arr = [];
for (let i=0, length= lists.length; i<length; i++) {
let item = lists[i];
arr.push(item.toUpperCase());
}
lists = arr;
}
上面這個函數(shù)雖然可以實現(xiàn)邏輯復(fù)用,但是有副作用,肯定是不適合用來做公共方法的,所以我們要優(yōu)化它:
let upperCaseLists = (value) => {
let arr = [];
for (let i=0, length= value.length; i<length; i++) {
let item = value[i];
arr.push(item.toUpperCase());
}
return arr;
}
使用可讀性更好的forEach來優(yōu)化:
let upperCaseLists = (value) => {
let arr = [];
value.forEach((item) => {
arr.push(item.toUpperCase());
})
return arr;
}
繼續(xù)用map進(jìn)一步優(yōu)化:
let upperCaseLists = (value) => {
return value.map((item) => item.toUpperCase())
}
是不是很簡潔?具體方法怎么優(yōu)化要根據(jù)實際情況和業(yè)務(wù)需求來。
參考
https://segmentfault.com/a/1190000007491981
https://cuggz.blog.csdn.net/article/details/113792471
https://www.redux.org.cn/
最后
純函數(shù)這個概念其實并不復(fù)雜,在沒有深入了解之前我們工作中也一定遇到過,也在不經(jīng)意間用過。只有要合理的去運用它,就是開發(fā)中的一把利器。

