相關文章
【函數式編程】基於JS 進行函數式編程(一)引入 | 什麼是函數式編程 | 函數式編程的優點
【函數式編程】基於JS進行函數式編程(二)高階函數 | 函數代替數據傳遞 | 函數是一等公民 | 閉包 | 使用高階函數實現抽象 | 數組的高階函數
【函數式編程】基於JS進行函數式編程(三)柯里化 | 偏函數 | 組合與管道
【函數式編程】基於JS進行函數式編程(四)函子 | MayBe函子 | Monad函子
什麼是函數式編程
引入
概念
我們知道,在數學中,函數可以有如下形式:
f(X) = Y
,即一個函數f ,以X作參數,返回輸出結果Y。
據此,我們可以歸納一個函數:
- 函數必須接受一個參數
- 函數必須返回一個值
函數應該根據接收到的參數(如:X)運行,而不是外部參數/環境
(關鍵)對於一個給定的X,只會輸出唯一的一個Y
(關鍵)
在編程語言中,函數式編程是一種範式,其能夠創建僅依賴輸入就可以完成自身邏輯的函數。這保證了當函數被多次調用時仍然返回相同的結果。同時,函數不會改變任何外部環境變量,這也將產生可緩存、可測試的代碼。
函數與方法
- 函數:一段可以通過其名稱被調用的代碼。它可以傳遞參數並返回值。
- 方法:一段必須通過
其名稱及其關聯對象的名稱被調用
的代碼。例如,在對象中定義的函數,就是該對象的方法。
引用透明性
前面我們提到: 對於一個給定的X,只會輸出唯一的一個Y
。即所有的函數,對於相同的輸入,將返回相同的值。這一性質被稱爲引用透明性
。
這使得併發代碼
和緩存
成爲可能。因爲,具有引用透明性的函數,只能依賴來自參數的輸入
,我們可以輕鬆地用多線程運行這樣的代碼,沒有任何鎖機制。
編程範式之 命令式 與 聲明式
首先我們要理解什麼是命令式,什麼是聲明式。
- 命令式:告訴編譯器
該做什麼
。即就告訴編輯器“如何做“
。如下,這段代碼告訴編譯器”獲取數組長度,循環數組,用索引獲取每一個數組元素“。
let array = [1,2,3];
for(i=0;i<array.length;i++)
console.log(array[i]);
- 聲明式:告訴編譯器
“做什麼”
。如何做的部分,將被抽象到普通函數(也稱”高階函數,如forEach()“)中。如下代碼,我們使用聲明式方式改寫上面的命令式代碼。
let array = [1,2,3];
array.forEach((e)=>console.log(e);)
由此可見,聲明式讓開發者只需要關注”做什麼“
部分。而無需關心怎麼做。
函數式編程的優點
純函數
大多數函數式編程的好處來自編寫純函數。
定義: 對給定的輸入返回相同的輸出的函數。
例如:
let double = (value) => value*2 ;
這是一個簡單的純函數,我們給它一個輸入,它返回相同的輸出。
可見,純函數遵循”引用透明性“
。
同時,純函數不應該改變任何外部環境變量
,即純函數不依賴任何外部變量。
併發代碼
純函數總是允許我們併發執行代碼。因爲純函數不會改變它的環境,這意味這我們不需要擔心同步問題。
例如:
let global = "something";
let func1 = (input) => {global="something2";}
let func2 = () => {if(global==="something2"){}}
改寫:
let func1 = (input,global) => {global="something2";}
let func2 = (global) => {if(global==="something2"){}} //將global變量作爲參數,由此可不依賴外部變量
可緩存
純函數總是爲給定輸入返回相同的輸出,那麼就可以對輸出進行緩存。
由此可見,純函數只專注做一件事!