在學習 JS 的過程當中時常會聽到一個名次——「函數式編程」,那麼究竟什麼是函數式編程,函數式編程又有什麼優勢,這就在這篇博客進行一個簡單的總結吧~html
主要內容:前端
首先,咱們放下編程的概念,咱們來看函數。git
函數的概念來自於數學,數學中的函數 f(x) = y
有一個很是重要的特色對於一個給定的 x,有惟一的 y 與其對應(這就是爲何橢圓曲線不是函數)github
然而在編程中,函數並不具備這個特色,舉個栗子:編程
let val = 1
function add(x){
return x + val
}
console.log(add(1)) // 2
val += 1
console.log(add(1)) // 3
複製代碼
能夠看到編程中的函數在參數相同的狀況下,容許有不一樣的返回值——只要依賴於函數外部的量設計模式
那麼當函數依賴於外部的變量(常量不會有這樣的問題),而且函數在多處調用,就有可能出現 bug,函數的調用結果可能會和預期結果相去甚遠緩存
而函數式編程就要求咱們規避這樣的狀況,讓全部函數對於相同的輸入的返回值相同,這樣的特性就叫作引用透明性,這就是函數式編程的核心特性!併發
一個符合引用透明性的函數的栗子:編程語言
function id(x){ return x; }
複製代碼
在上文中提到過,編程語言中的函數大可能是不知足數學中的函數的概念的,so 咱們將知足數學函數條件的函數稱爲「純函數」函數式編程
函數式編程的優勢大多都來自於純函數
除了測試人員進行的全方位測試外,咱們在開發過程當中每每要對本身寫的代碼進行模塊測試
在開發中,我受非純函數迫害已久,因爲它依賴了外部變量(好比存儲在 localStorage 中的數據)我不得不三番五次去檢查這些外部變量是否在某個過程當中被改變甚至是刪除
let val = 1
function add(x){
return x + val
}
console.log(add(1)) // 2
// 在未知因素影響下 val被改變
console.log(add(1)) // 預期結果 2,實際輸出 emmmmm
複製代碼
若是我沒有注意外部依賴而是一頭扎進函數邏輯裏,可能永遠都找不到這個bug
雖然咱們都知道 JS 是一門單線程語言(關於JS的執行能夠參見->技術總結——JS的執行順序),可是咱們爲了提升前端的性能可能會經過 WebWorker
來併發執行多個任務,或者在 Node 環境下 JS 併發執行函數
這個時候就是對非純函數的一個很大的考驗:
let global = "全局變量"
let func1 = ()=>{
global = "全局變量被改變了"
// 一些邏輯
}
let func2 = ()=>{
if(global = "全局變量"){
return true
}
}
複製代碼
上面的兩個函數都依賴於外部的global,當它們併發執行,func1就會對func2產生影響,若是將它們變爲純函數就不會有這樣的問題:
let global = "全局變量"
let func1 = (x)=>{
x = "全局變量被改變了"
// 一些邏輯
}
let func2 = (x)=>{
if(x = "全局變量"){
return true
}
}
複製代碼
當咱們的函數都是純函數,而咱們又會屢次調用函數,咱們就能夠對函數對象進行一個緩存
好比咱們須要大量計算數字的4次方,咱們能夠創建一個映射表用來緩存函數的執行結果:
// 映射表
let fourTimesTable = {};
let fourTimes = (x){
return x*x*x*x
}
// 檢查表中是否有 2 的四次方,若是有就返回,若是沒有就執行函數避免運算
fourTimesTable.hasOwnProperty(2)?
fourTimesTable[2]:
fourTimesTable[2] = fourTimes(2)
複製代碼
管道過濾器是一種很經典的設計模式,咱們能夠將這種模式和函數式編程結合起來
在管道和過濾器軟件體系結構中,每一個模塊都有一組輸入和一組輸出。每一個模塊從它的輸入端接收輸入數據流,在其內部通過處理後,按照標準的順序,將結果數據流送到輸出端,以達到傳遞一組完整的計算結果實例的目的。
在這種結構中,各模塊之間的鏈接器充當了數據流的導管,將一個過濾器的輸出傳到下一個過濾器的輸入端。因此,這種鏈接器稱爲「管道」。
咱們也能夠將這樣的私用運用在函數式編程中,將一個個函數做爲過濾器,經過函數的組合,造成一條數據處理的通路:
// 參數累加
function add(...args){
let result = args.reduce((prev, cur, index, arr)=> {
return prev + cur;
})
return result
}
// 參數累乘
function times(...args){
let result = args.reduce((prev, cur, index, arr)=> {
return prev * cur;
})
return result
}
let arr1=[1,3,6],arr2=[2,5,21],arr3=[3,7,8,27,4]
// 三組數據,要求組內累乘,而後結果累加
add(times(...arr1),times(...arr2),times(...arr3))
// 三組數據,要求組內累加,而後結果累乘
times(add(...arr1),add(...arr2),add(...arr3))
複製代碼
對函數式編程的初探暫止於此,進一步學習後再作總結~
若是有什麼問題,意見,建議歡迎評論;若是以爲我寫的不錯,那就點個贊吧~