面向對象編程和麪向過程編程都是編程範式,函數式編程也是一種編程範式,意味着它們都是軟件構建的思惟方式。與命令式或面向對象代碼相比,函數式代碼傾向於更簡潔、更可預測以及更易於測試。javascript
是指經過複合純函數來構建軟件的過程,它避免了共享的狀態、易變的數據、以及反作用。函數式編程是聲明式而不是命令式,而且應用程序狀態經過純函數流轉。java
純函數是知足以下條件的函數:git
JS中純函數的例子:github
String.prototype.toUpperCase Array.prototype.map Function.prototype.bind 複製代碼
JS中非純函數的例子:編程
Date.now Math.random Array.prototype.sort document.body.appendChild 複製代碼
純函數的好處:數組
函數複合是結合兩個或多個函數,從而產生一個新函數或進行某些計算的過程。markdown
在 JavaScript 中至關於執行 f(g(x))。網絡
共享狀態 的意思是:任意變量、對象或者內存空間存在於共享做用域(包括全局做用域和閉包做用域)下,或者做爲對象的屬性在各個做用域之間被傳遞。數據結構
一般,在面向對象編程中,對象以添加屬性到其餘對象上的方式在做用域之間共享。與面向對象編程不一樣,函數式編程避免共享狀態,它依賴於不可變數據結構和純粹的計算過程來從已存在的數據中派生出新的數據。
共享狀態的一個常見問題是改變函數調用次序函數調用的次序可能會改變函數調用的結果,進而可能致使一連串的錯誤:閉包
const x = { val: 2 }; const x1 = () => x.val += 1; const x2 = () => x.val *= 2; x1(); // -> 3 x2(); // -> 6 複製代碼
下面的例子與上面的相同,除了函數調用的次序不一樣:
const x = { val: 2 }; const x1 = () => x.val += 1; const x2 = () => x.val *= 2; x2(); // -> 4 x1(); // -> 5 複製代碼
若是避免共享狀態,就不會改變函數內容,或者改變函數調用的時序不會波及和破壞程序的其餘部分:
const x = { val: 2 }; const x1 = x => Object.assign({}, x, { val: x.val + 1}); const x2 = x => Object.assign({}, x, { val: x.val * 2}); x1(x); // -> 3 x2(x); // -> 4 /** x2(x); // -> 4 x1(x); // -> 3 */ 複製代碼
在其餘類型的語言中,變量每每用來保存"狀態"。而函數式編程只是返回新的值,不修改系統變量,便是無破壞性的數據轉換。
反作用是指除了函數返回值之外,任何在函數調用以外觀察到的應用程序狀態改變。
反作用包括:
在函數式編程中,反作用被儘量避免。
函數式編程是一個聲明式範式,意思是說程序邏輯不須要經過明確描述控制流程來表達。
命令式:
let list = [1, 2, 3, 4]; let map1 = []; for (let i = 0; i < list.length; i++) { map1.push(list[i] * 2); } 複製代碼
聲明式:
let list = [1, 2, 3, 4]; let map2 = list.map(x => 2 * x); 複製代碼
再來看聲明式例子中引出的兩個重要概念:
彼此之間存在某種關係的概念、事物、對象等等,都構成"範疇"。
範疇的數學模型簡單理解就是:"集合 + 函數"。
在函數式編程中,一般使用functors以及使用高階函數抽象來建立通用功能函數,以處理任意數值或不一樣類型的數據。
高階函數指的是一個函數以函數爲參數,或以函數爲返回值,或者既以函數爲參數又以函數爲返回值。
高階函數經常使用於:
經過指定部分參數來產生一個新定製函數的形式就是偏函數。
const isType = function (type) { return function (obj) { return toString.call(obj) == '[object' + type + ']'; }; }; const isString = isType('string'); const isFunction = isType('function'); 複製代碼
柯里化是將一個多參數函數轉換成多個單參數函數。
// 柯里化以前 function add(x, y) { return x + y; } add(1, 2) // 3 // 柯里化以後 function addX(y) { return function (x) { return x + y; }; } addX(2)(1) // 3 複製代碼
若是一個值要通過多個函數,才能變成另一個值,就能夠把全部中間步驟合併成一個函數,這叫作"函數的複合"。
一個簡單的函數的複合例子:
const compose = function (f, g) { return function (x) { return f(g(x)); }; } 複製代碼
實現一個高階函數用來減小非純函數:
function batch (fn) { return function (target, ...args) { if (target.length >= 0) { return Array.from(target).map(item => fn.apply(this, [item, ...args])); } else { return fn.apply(this, [target, ...args]); } } } 複製代碼
例如:兩個非純函數 -> batch(fn) -> 一個非純函數
函數式編程偏好: