為什麼使用函數語言程式設計
1. React、vue3都支援函數語言程式設計
2. 函數語言程式設計可以拋棄this
3. 打包中可更好利用tree taking過濾沒有使用的程式碼
4. 方便單元測試,方便並行處理
5. 使程式碼更簡潔,更通用,擴充套件性更好
什麼是函數語言程式設計
函數語言程式設計(Functional Programming,FP)是程式設計正規化之一s,與物件導向程式設計並列
函數語言程式設計的思維方式:對運算過程對抽象
函數語言程式設計的函式是數學中的函式即對映關係,如y=sin(x)
相同輸入會得到相同輸出(純函式),如陣列的slice方法
函數語言程式設計是描述資料(函式)之間的對映
相關概念
1. 函式是一等公民
函式可儲存到變數中,可作為函式的引數,可作為返回值
2. 高階函式
概念:函式可作為引數傳遞給其他函式,函式可作為返回值
意義:可抽象具體的細節,而只關注輸入與輸出,高階函式可抽象通用問題,更具有擴充套件性
常用的高階函式:forEach, map, reduce, every, some, sort, filter, find等
3. 閉包
概念:函式和其周圍的狀態的引用捆綁在一起形成閉包,簡而言之,在裡層作用域中使用外層作用域擁有的變數,即形成閉包
本質:函式被放到呼叫棧執行後,會銷燬此函式,但當其中的變數被外部引用時,不會被銷燬和釋放,故里層函式可使用外層函式的變數
例子:es6的let定義變數會形成閉包,例如:
for ( let i = 0 ; i < 10 ; i++ ) {
setTimeout ( ( ) => {
console. log ( i) ;
} , 10 )
}
此時列印值為1-9,但是如果使用var定義或let定義放到for迴圈的上方,則不能形成閉包,列印值就為10
4. 純函式
概念:相同輸入會得到相同輸出,而且沒有任何副作用,所謂副作用就是不可預測的結果,比如函式中使用服務端返回的資料,全域性變數等
純函式類似於數學中的計算公式,比如y=sin(x), x值只要確定,那麼y的值就是定值
示例:陣列的slice是純函式,splice不是純函式
函數語言程式設計不會保留中間過程,只會有返回值,可以把返回值去當引數傳給其他函式
好處:
1. 可快取,因為相同輸入有相同輸出,所以可以把返回值快取下來,不用引數相同時,去多次執行
2. 方便自我除錯,對超出預期對結果可通過增加斷言函式去列印結果
3. 並行處理,因為純函式只依賴引數,而且不會修改全域性變數,所以就算是多執行緒或多處都呼叫,也無須擔心出現預期外的錯誤
副作用:會讓純函式變為不純,比如配置檔案,資料庫,全域性變數,dom
副作用會降低函式的通用性、擴充套件性、重用性,但副作用不可避免,比如dom互動等
5.柯里化
概念:當函式存在多個引數時,可呼叫函式時傳遞一部分引數,返回個新函式,再呼叫新函式時,傳遞剩下的引數,當引數全部傳入後,再執行此函式
例子:
var _ = require ( "lodash" ) ;
var getSums = ( a, b, c) => { return a + b + c} ;
var getSumsByCurry = _. curry ( getSums) ;
getSumsByCurry ( 1 ) ( 2 ) ( 3 ) ;
手寫curry:
var curry = function ( fn) {
return function f ( ... args) {
if ( args. length >= fn. length ) {
return fn ( ... args) ;
}
return function ( ... ab) {
return f ( ... args, ... ab) ;
}
}
}
總結:柯里化可以得到快取某些公共引數的函式,讓函式的粒度更小,更靈活,可以把多個引數的函式變為只傳一個引數的函式,方便函式組合時使用
6.函式組合
函式組合可以把多個細粒度的函式組合成新的函式,如果一個函式需要其他函式的返回值作為引數,這時就可以把這些函式組合成一個新的函式
函式式組合可最大限度的去重新利用細粒度的函式,提高函式的重複利用
手寫函式組合,從右到左執行
function compose ( ... args) {
return function ( value) {
return args. reverse ( ) . reduce ( function ( ret, fn) {
return fn ( ret)
} , value)
}
}
var fn = compose ( a ( ) , b ( ) ) ;
fn ( 1 )
7.函子(functor)
概念:是一個特殊的容器,由物件實現,該物件必須有map方法,map方法可以對值進行處理
class Container {
static of ( value) {
return new Container ( value)
}
constructor ( value) {
this . _value = value
}
map ( fn) {
return Container. of ( fn ( this . _value) )
}
}
let r = Container. of ( 5 )
. map ( x => x + 2 )
. map ( x => x * x)
總結:
可處理函式對副作用,不在函式內呼叫副作用對函式,而是暴露出,比如對外暴露run方法,當執行run時,才處理某個傳入的函式
map方法會呼叫傳入對函式,返回值為處理過的新的函子
函子就是約定好有map特定作用函式的物件,把傳入的value封裝,當需要改變value時,通過map方法去實現
型別:
MayBe函子:相容非空
Either函子:類似if else
IO函子:value為函式,當有需要時,由呼叫方手動呼叫
Pointed函子:實現了of方法當函子
Monad函子:使巢狀函子時,把呼叫方式拍扁