什么是純函數(shù)
純函數(shù)是函數(shù)式編程中非常重要的一個(gè)概念压汪,簡(jiǎn)單來(lái)說(shuō),就是一個(gè)函數(shù)的返回結(jié)果只依賴于它的參數(shù),并且在執(zhí)行過(guò)程中沒(méi)有副作用,我們就把這個(gè)函數(shù)叫做純函數(shù)
劃重點(diǎn):
- 函數(shù)的返回結(jié)果只依賴于它的參數(shù)
- 函數(shù)執(zhí)行過(guò)程中沒(méi)有副作用
首先來(lái)看第一點(diǎn):函數(shù)的返回結(jié)果只依賴于它的參數(shù)
const a = 1
const impure = (b)=>a + b
impure(2) // 3
上面代碼中热康, impure函數(shù)不是一個(gè)純函數(shù),因?yàn)樗姆祷亟Y(jié)果依賴于外部變量a, 因?yàn)閍是有可能變化的劣领, 所以我們不能保證 impure(2)的值永遠(yuǎn)是3. 雖然impure函數(shù)的代碼沒(méi)有變化姐军, 傳入的參數(shù)也沒(méi)有變化, 但它的返回值是不可預(yù)料的尖淘。 我們?cè)賮?lái)改一下:
const a = 1
const pure = (x, b) => x + b
pure(1,2) //3
現(xiàn)在庶弃, pure 滿足純函數(shù)的第一個(gè)條件,它的返回結(jié)果只依賴于它的參數(shù) x 和 b, 就是說(shuō)德澈, 只要代碼不變, pure(1,2)的返回值永遠(yuǎn)是3.
這就是純函數(shù)的第一個(gè)條件: 函數(shù)的返回結(jié)果只依賴于它的參數(shù)
接下來(lái)解釋第二點(diǎn):函數(shù)執(zhí)行中沒(méi)有副作用
副作用指的是: 在計(jì)算結(jié)果的過(guò)程中固惯,系統(tǒng)狀態(tài)的一種變化梆造, 或者與外部事件進(jìn)行觀察的交互。 再看一個(gè)例子:
var values = { a: 1 };
function impureFunction ( items ) {
var b = 1;
items.a = items.a * b + 2;
return items.a;
}
var c = impureFunction( values );
values.a // 3
在上面的代碼中葬毫,我們改變了參數(shù)對(duì)象中的屬性a, 由于我們定義的函數(shù)改變的對(duì)象在我們的函數(shù)作用域之外镇辉,導(dǎo)致這個(gè)函數(shù)稱為“不純”函數(shù)
var values = { a: 1 };
function pureFunction ( a ) {
var b = 1;
a = a * b + 2;
return a;
}
var c = pureFunction( values.a );
values.a // 1
上面的代碼,我們只計(jì)算了作用域內(nèi)的局部變量贴捡, 沒(méi)有任何作用域外部的變量被改變忽肛, 因此這個(gè)函數(shù)是 “純函數(shù)”
除了修改外部的變量, 一個(gè)函數(shù)在執(zhí)行過(guò)程中還有很多方式產(chǎn)生外部可觀察的變化烂斋,比如說(shuō)調(diào)用 DOM API修改頁(yè)面屹逛, 或者你發(fā)送了Ajax請(qǐng)求, 還有調(diào)用window.reload刷新瀏覽器汛骂, 甚至是console.log往控制臺(tái)打印數(shù)據(jù)也是副作用罕模。
總結(jié)一些副作用:
- 更改輸入
- console.log
- HTTP請(qǐng)求(AJAX,fetch)
- 更改文件系統(tǒng)
- DOM查詢
純函數(shù)很嚴(yán)格, 也就是說(shuō)你幾乎除了計(jì)算數(shù)據(jù)以外什么都不能干帘瞭, 計(jì)算的時(shí)候還不能依賴除了函數(shù)參數(shù)以外的數(shù)據(jù)淑掌。
我們常用的JS中的兩個(gè)方法: splice和slice來(lái)舉一個(gè)例子
var array1 = [0,1,2,3,4,5,6];
var array2 = [0,1,2,3,4,5,6];
var spliceArray = array1.splice(0,2);
var sliceArray = array2.slice(0,2);
console.log('array1: ' + array1);
console.log('spliceArray: ' + spliceArray);
console.log('array2: ' + array2);
console.log('sliceArray: ' + sliceArray);
運(yùn)行結(jié)果:
array1: 2,3,4,5,6
spliceArray: 0,1
array2: 0,1,2,3,4,5,6
sliceArray: 0,1
可以看到, slice和splice的作用是大致相同的蝶念, 但是slice改變了原數(shù)組抛腕, 而slice卻沒(méi)有, 實(shí)際開(kāi)發(fā)中媒殉, slice這種不改變?cè)瓟?shù)組的方式更安全一些担敌,改變?cè)瓟?shù)組,是一種副作用
非純函數(shù)帶來(lái)的副作用
function getName(obj){
return obj.name;
}
function getAge(obj){
return obj.age;
}
function sayHi(person){
console.log('I am' + getName(person) + ',and I am' + getAge(person) + 'years old');
}
var Tom = { name: 'TOM', age: 26};
sayHi(Tom);
這里的sayHi不是純函數(shù)廷蓉, 它依賴于 getName柄错, getAge兩個(gè)函數(shù), 如果我不小心改變了其中某個(gè)函數(shù)的功能, 這將使得sayHi中國(guó)函數(shù)出現(xiàn)錯(cuò)誤售貌。 當(dāng)代碼變得復(fù)雜给猾,且由多人維護(hù)的時(shí)候,bug調(diào)試會(huì)變得非常復(fù)雜
使用純函數(shù)的優(yōu)點(diǎn)
1. 可復(fù)用性
純函數(shù)僅依賴于傳入的參數(shù)颂跨,這意味著你可以隨意將這個(gè)函數(shù)移植到別的代碼中敢伸, 只需要提供它需要的參數(shù)即可。 如果是非純函數(shù)恒削, 有可能你需要一根香蕉池颈,卻需要將整個(gè)香蕉樹(shù)都搬過(guò)去
2. 可測(cè)試性
純函數(shù)非常容易進(jìn)行單元測(cè)試,因?yàn)椴恍枰紤]上下文環(huán)境钓丰, 只需要考慮輸入和輸出
3. 并行代碼
純代碼是健壯的躯砰, 改變執(zhí)行次序不會(huì)對(duì)系統(tǒng)造成影響, 因此純函數(shù)的操作可以并行執(zhí)行携丁。
總結(jié)
- 不產(chǎn)生副作用琢歇, 并且相同的輸入總是返回相同的輸出,那么這個(gè)函數(shù)是純函數(shù)梦鉴。
- 副作用包括但不限于: 更改輸入李茫,HTTP請(qǐng)求, 磁盤(pán)寫(xiě)入肥橙,打印
- 可以將函數(shù)輸入拷貝一份用于更改魄宏,不要去動(dòng)原來(lái)的數(shù)據(jù)。
- 擴(kuò)展運(yùn)算符語(yǔ)法(...語(yǔ)法)是一個(gè)復(fù)制對(duì)象或者數(shù)據(jù)的簡(jiǎn)便方法存筏。
雖然純函數(shù)有很多優(yōu)點(diǎn)宠互, 但也要避免濫用的情況,函數(shù)越純椭坚,對(duì)環(huán)境依賴越小名秀, 往往意味著要傳入更多的參數(shù)。 我們的最終目的是: 讓你的代碼盡可能簡(jiǎn)單易懂和靈活藕溅。盡量在合適的場(chǎng)景使用它匕得。