摘要:于是就有了即學(xué)即用這個系列的文章。系列第一篇,就從純函數(shù)開始,由于我是前端方向,所以就從語言中的純函數(shù)說起。并行代碼純函數(shù)是健壯的,改變執(zhí)行次序不會對系統(tǒng)造成影響,因此純函數(shù)的操作可以并行執(zhí)行。
最近一直在思考如何通過文章或者培訓(xùn)快速提升團隊的編碼能力,總結(jié)下來其實技術(shù)的學(xué)習(xí)分為兩類:一種是系統(tǒng)性的學(xué)習(xí),比如學(xué)習(xí)一門語言,學(xué)習(xí)一個開發(fā)框架,這更需要自己從入門到進階再到實踐一步步系統(tǒng)性的學(xué)習(xí),單靠幾篇文章或者幾次培訓(xùn),效果并不明顯;還有一種是技巧性的學(xué)習(xí),比如某些編程實踐、設(shè)計原則,其實并沒有多么復(fù)雜,但是不知道就不會用,知道了就會有意識去用,就好比玩微信游戲跳一跳,在某些方塊上停留一段時間就會獲得加分,不知道的時候根本想不到,知道了以后想不用都難。于是就有了《即學(xué)即用》這個系列的文章。
系列第一篇,就從純函數(shù)開始,由于我是前端方向,所以就從JavaScript語言中的純函數(shù)說起。
什么是純函數(shù)純函數(shù)是函數(shù)式編程中非常重要的一個概念,簡單來說,就是一個函數(shù)的返回結(jié)果只依賴于它的參數(shù),并且在執(zhí)行過程中沒有副作用,我們就把這個函數(shù)叫做純函數(shù)。
下面我們來劃重點:
函數(shù)的返回結(jié)果只依賴于它的參數(shù)
函數(shù)執(zhí)行過程中沒有副作用
首先來解釋第一點:函數(shù)的返回結(jié)果只依賴于它的參數(shù)
const a = 1 const impure = (b)=>a + b impure(2) // 3
上面代碼中,impure函數(shù)不是一個純函數(shù),因為它的返回結(jié)果依賴外部變量a,因為a是有可能變化的,所以我們不能保證impure(2)的值永遠是3。雖然impure函數(shù)的代碼沒有變化,傳入的參數(shù)也沒有變化,但它的返回值是不可預(yù)料的。我們再來改寫一下:
const a = 1 const pure = (x, b) => x + b pure(1,2) //3
現(xiàn)在,pure的返回結(jié)果只依賴于它的參數(shù)x和b,就是說,只要代碼不變,pure(1, 2)的返回值永遠是3。
這就是純函數(shù)的第一個條件:函數(shù)的返回結(jié)果只依賴于它的參數(shù)
接下來解釋第二點:函數(shù)執(zhí)行中沒有副作用
副作用是指:在計算結(jié)果的過程中,系統(tǒng)狀態(tài)的一種變化,或者與外部世界進行的可觀察的交互。我們再看一個例子:
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ù)對象中的一個屬性。由于我們定義的函數(shù)改變的對象在我們的函數(shù)作用域之外,導(dǎo)致這個函數(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
上面的代碼,我們只計算了作用域內(nèi)的局部變量,沒有任何作用域外部的變量被改變,因此這個函數(shù)是“純函數(shù)”。
除了修改外部的變量,一個函數(shù)在執(zhí)行過程中還有很多方式產(chǎn)生外部可觀察的變化,比如說調(diào)用 DOM API 修改頁面,或者你發(fā)送了 Ajax 請求,還有調(diào)用 window.reload刷新瀏覽器,甚至是 console.log 往控制臺打印數(shù)據(jù)也是副作用。
純函數(shù)很嚴(yán)格,也就是說你幾乎除了計算數(shù)據(jù)以外什么都不能干,計算的時候還不能依賴除了函數(shù)參數(shù)以外的數(shù)據(jù)。
我們再來用JavaScript中常用的兩個方法slice和splice來舉一個例子:
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);
運行結(jié)果:
array1: 2,3,4,5,6 spliceArray: 0,1 array2: 0,1,2,3,4,5,6 sliceArray: 0,1
可以看到,slice和splice的作用是大致相同的,但是splice改變了原數(shù)組,而slice卻沒有,實際開發(fā)中,slice這種不改變原數(shù)組的方式更安全一些,改變原始數(shù)組,是一種副作用。
非純函數(shù)帶來的副作用既然我們推薦純函數(shù),那么肯定是因為非純函數(shù)有缺陷。我們看下面的代碼:
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兩個函數(shù),如果我不小心改變了其中某個函數(shù)的功能,這將使得sayHi這個函數(shù)出現(xiàn)錯誤。當(dāng)網(wǎng)頁變得復(fù)雜,且由多人維護的時候,bug調(diào)試會變得非常復(fù)雜。
使用純函數(shù)的優(yōu)點 1. 可復(fù)用性純函數(shù)僅依賴于傳入的參數(shù),這意味著你可以隨意將這個函數(shù)移植到別的代碼中,只需要提供踏需要的參數(shù)即可。如果是非純函數(shù),有可能你需要一根香蕉,卻需要將整個香蕉樹搬過去。
2. 可測試性純函數(shù)非常容易進行單元測試,因為不需要考慮上下文環(huán)境,只需要考慮輸入和輸出。
3. 并行代碼純函數(shù)是健壯的,改變執(zhí)行次序不會對系統(tǒng)造成影響,因此純函數(shù)的操作可以并行執(zhí)行。
總結(jié)雖然純函數(shù)有很多優(yōu)點,但也要避免濫用的情況。函數(shù)越純,對環(huán)境依賴越小,往往意味著要傳入更多的參數(shù)。我們的最終目的是:讓你的代碼盡可能簡單易懂和靈活。這篇文章主要介紹了JavaScript中純函數(shù)的概念,但是在很多其他開發(fā)語言中,純函數(shù)的概念是一樣通用的,比如筆者正在自學(xué)的JAVA,歡迎大家針對各種語言中對純函數(shù)的理解和我一起討論。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/92911.html
閱讀 908·2021-10-13 09:39
閱讀 1498·2021-10-11 10:57
閱讀 2606·2019-08-26 13:53
閱讀 2551·2019-08-26 12:23
閱讀 3703·2019-08-23 18:30
閱讀 3761·2019-08-23 18:08
閱讀 2539·2019-08-23 18:04
閱讀 2970·2019-08-23 16:28