函數式編程是一種編程範式,是一種構建計算機程序結構和元素的風格,它把計算看做是對數學函數的評估,避免了狀態的變化和數據的可變,與函數式編程相對的是命令式編程。咱們有這樣一個需求,給數組的每一個數字加一:javascript
// 數組每一個數字加一, 命令式編程 let arr = [1, 2, 3, 4]; let newArr = []; for(let i = 0; i < arr.length; i++){ newArr.push(arr[i] + 1); } console.log(newArr); // [2, 3, 4, 5]
這段代碼結果沒有問題,可是無法重用。咱們換一個思惟,這裏麪包含的操做其實就兩個,一個是遍歷數組,一個是成員加一。咱們把這兩個方法拆出來:java
// 先拆加一出來 let add1 = x => x +1; // 而後拆遍歷方法出來,經過遍歷返回一個操做後的新數組 // fn是咱們須要對每一個數組想進行的操做 let createArr = (arr, fn) => { const newArr = []; for(let i = 0; i < arr.length; i++){ newArr.push(fn(arr[i])); } return newArr; } // 用這兩個方法來獲得咱們指望的結果 const arr = [1, 2, 3, 4]; const newArr = createArr(arr, add1); console.log(newArr); // [2, 3, 4, 5], 結果仍然是對的
這樣拆分後,若是咱們下次的需求是對數組每一個元素乘以2,咱們只須要寫一個乘法的方法,而後複用以前的代碼就行:git
let multiply2 = x => x * 2; // 調用以前的createArr const arr2 = [1, 2, 3, 4]; const newArr2 = createArr(arr2, multiply2); console.log(newArr2); // [2, 4, 6, 8], 結果是對的
事實上咱們的加一函數只能加一,也很差複用,它還能夠繼續拆:github
// 先寫一個通用加法,他接收第一個加數,返回一個方法 // 返回的這個方法接收第二個加數,第一個加數是上層方法的a // 這樣當咱們須要計算1+2是,就是add(1)(2) let add = (a) => { return (b) => { return a + b; } } // 咱們也能夠將返回的函數賦給一個變量,這個變量也就變成一個能特定加a的一個方法 let add1 = add(1); let res = add1(4); console.log(res); // 5
因此函數式編程就是將程序分解爲一些更可重用、更可靠且更易於理解的部分,而後將他們組合起來,造成一個更易推理的程序總體。編程
純函數是指一個函數,若是它的調用參數相同,則永遠返回相同的結果。它不依賴於程序執行期間函數外部任何狀態或數據的變化,只依賴於其輸入參數。同時函數的運行也不改變任何外部數據,它只經過它的返回值與外部通信。數組
下面這個函數就不是純函數,由於函數內部須要的discount
須要從外部獲取:函數式編程
let discount = 0.8; const calPrice = price => price * discount; let price = calPrice(200); // 160 // 當discount變了,calPrice傳一樣額參數,結果不同,因此不純 discount = 0.9; price = calPrice(200); // 180
要改成純函數也很簡單,將discount
做爲參數傳遞進去就好了函數
const calPrice = (price, discount) => price * discount;
純函數能夠保證代碼的穩定性,由於相同的輸入永遠會獲得相同結果。不純的函數可能會帶來反作用。spa
函數反作用是指調用函數時除了返回函數值以外,還對主調用函數產生附加的影響,好比修改全局變量或者外部變量,或者修改參數。這可能會帶來難以查找的問題並下降代碼的可讀性。下面的foo
就有反作用,當後面有其餘地方須要使用a,可能就會拿到一個被污染的值code
let a = 5; let foo = () => a = a * 10; foo(); console.log(a); // 50
除了咱們本身寫的函數有反作用外,一些原生API也可能有反作用,咱們寫代碼時應該注意:
咱們的目標是儘量的減小反作用,將函數寫爲純函數,下面這個不純的函數使用了new Date
,每次運行結果不同,是不純的:
要給爲純函數能夠將依賴注入進去,所謂依賴注入就是將不純的部分提取出來做爲參數,這樣咱們可讓反作用代碼集中在外部,遠離核心代碼,保證核心代碼的穩定性
// 依賴注入 const foo = (d, log, something) => { const dt = d.toISOString(); return log(`${dt}: ${something}`); } const something = 'log content'; const d = new Date(); const log = console.log.bind(console); foo(d, log, something);
因此減小反作用通常的方法就是:
1. 函數使用參數進行運算,不要修改參數 2. 函數內部不修改外部變量 3. 運算結果經過返回值返回給外部
下面是一個可變的例子:
若是咱們必定要修改這個參數,咱們應該將這個參數進行深拷貝後再操做,這樣就不會修改參數了:
文章的最後,感謝你花費寶貴的時間閱讀本文,若是本文給了你一點點幫助或者啓發,請不要吝嗇你的贊和GitHub小星星,你的支持是做者持續創做的動力。
做者博文GitHub項目地址: https://github.com/dennis-jiang/Front-End-Knowledges