函數式編程 ( Functional Programming ) 是一種以函數爲基礎的編程方式和代碼組織方式,從程序員的思路上來講,就是將程序拆分並抽象成多個函數,再組裝回去。前端
在JavaScript這門語言中,函數是一等公民,函數能夠做爲函數的參數,函數也能夠返回一個函數,所以適合函數式編程風格。程序員
函數式風格的編程,長於數據的處理,且所謂「處理」並不改變數據自己。JavaScript中,數據老是以對象的形式出現,也就是說,函數式的編程對於「接收對象A→處理→處理→處理→得出新對象B」這樣的流程能夠很優雅地實現,且不影響對象A自己。編程
對於JavaScript程序員(不管是前端仍是後端),最多見且實用的編程模式固然是抽象出類(以構造函數的形式)並實例化,而後在實例上調用方法。函數式風格的編程不是爲了取代這種編程模式,而是爲了與之造成互補。後端
一些特色:數組
不改變輸入的數據安全
不耦合,也就是說,函數儘可能不影響甚至知曉外部的狀態數據結構
保持數據組織形式的簡單,例如,儘可能使用JavaScript原生的數據結構(對象、數組等)閉包
在函數式編程中,任何代碼能夠都是函數,且要求具備返回值,以下示例ide
// 非函數式 var title = "Functional Programming"; var saying = "This is not"; console.log(saying + title); // => This is not Functional Programming // 函數式 var say = title => "This is " + title; var text = say("Functional Programming"); // => This is Functional Programming
純函數在這裏指函數內外間是「無」關聯的。主要有下面兩點
沒有反作用(side effect)
不會涉及到外部變量的使用或修改
引用透明
函數內只會依賴傳入參數,在任什麼時候候對函數輸入相同的參數時,總能輸出相同的結果函數式編程
// 非純函數(函數內依賴函數外的變量值) var title = "Functional Programming"; var say = ()=> "This is not" + title; // <= 依賴了全局變量 title // 純函數 var say = (title)=>"This is " + title; // <= 依賴了以參數 title 傳入 say("Functional Programming");
這裏主要是指變量值的不可變。當須要基於原變量值改變時,可經過產生新的變量來確保原變量的不變性,以下
// 可變數據 var arr = ["Functional", "Programming"]; arr[0] = "Other"; // <= 修改了arr[0]的值 console.log(arr) // => ["Other", "Programming"] // 變量arr值已經被修改 // 不可變數據 var arr = ["Functional", "Programming"]; // 獲得新的變量,不修改了原來的值 var newArr = arr.map(item => { if(item === "Functional"){ return "Other"; } else { return item; } }) console.log(arr); // => ["Functional", "Programming"] 變量arr值不變 console.log(newArr); // => ["Other", "Programming"] 產生新的變量newArr
之因此使用這種不變值,除了更好的函數式編程外,還可以維持線程安全可靠,實際上也能讓代碼更加清晰。
設想,若是你定義了一個變量A,A在其餘地方被其餘人修改了,這樣是不方便定位A的當前值的。
使用 map, reduce 等數據處理函數
強大的 JavaScript 有着愈來愈多的高能處理數據函數,其中包含了 map、 reduce、 filter 等。
map 可以對原數組中的值進行逐個處理併產生新的數組,一個簡單例子
// map var data = [1, 2, 3]; var squares = data.map( (item, index, array) => item * item ); console.log(squares); // => [1, 4, 9] console.log(data);// => [1, 2, 3] data 仍是那個 data
reduce 可以對原數組中的各個值進行結合處理,來產生新的值,以下面例子中,previous 表明上一個值,current 表明當前值,reduce 函數能夠傳入第二個參數做爲 previous 初始值,不傳時則 previous 初始值爲數組中第一個值。
// reduce var sum = [1, 2, 3].reduce( (previous, current, index, array) => previous + current ); console.log(sum); // => 6
柯里化 是將多參函數轉換成一系列的單參函數。結合下面例子來講明下
// 一個多參函數 var add = (a, b) => a + b; add(1, 2); // => 3
將上面的多參函數進行柯里化,以下
// 柯里化函數 var add = a => b => a + b;
上面柯里化後的函數調用方式也有所轉變,第一次傳入一個參數返回了一個函數,再傳入參數則完成總體的調用,這也是利用的閉包的特性
var add1 = add(1); add1(2); // => 3
柯里化後的函數,也能夠應用在生產 「 函數 」 上,以下示例
var say = title => type => title + " is " + type; var sayFP = say("Functional Programming"); var sayOther = say("Other Programming"); sayFP("good"); // => Functional Programming is good sayOther("good"); // => Other Programming is good
顧名思義,組合函數是將多個函數進行組合成一個函數。舉個例子
var compose = (fn1, fn2) => (arg) => fn1(fn2(arg)); var a = arg => arg + 'a'; var b = arg => arg + 'b'; var c = compose(a, b); // 將a,b函數進行組合 c('c'); // => cba
上面示例中,當調用組合函數 c 時,傳入的參數會通過 b 函數,接着將 b 函數的返回值做爲 a 函數的參數值,從而輸出最終結果。組合函數 c 就像管道同樣,將水流( 返回值 )流經各個函數中進行處理。