什麼是函數式編程?編程
函數式編程的前置知識segmentfault
高階函數數組
閉包閉包
閉包的案例app
ps:對總體不太瞭解的先看有線函數式編程整體設計,再閱讀下面的內容~~~
函數式編程是一個很是古老的概念。函數式編程
函數式編程,縮寫FP,是一種編程範式,也是一種編程風格,和麪向對象是並列的關係。函數式編程咱們能夠認爲是一種思惟模式,加上實現方法。其思惟方式就是把現實世界事物和事物之間的聯繫抽象到程序世界(是對運算過程進行抽象
)函數
常據說的編程範式還有面向過程編程(按照步驟來實現)、面向對象編程(把現實中的事物抽象成類和對象,經過封裝、繼承和多態來演示不一樣事物之間的聯繫)。post
從思惟方式上來講 面向對象編程是對事物的抽象,而函數式編程是對運算過程的抽象測試
// 非函數式 let num1 = 2 let num2 = 3 let sum = num1 + num2 console.log(sum) // 函數式 function add(n1, n2) { return n1 + n2 } let sum = add(2, 3) console.log(sum)
在JS中函數就是一個普通的對象,咱們能夠把函數存儲到變量/數組中,它還能夠做爲另外一個函數的參數和返回值,甚至咱們能夠在程序運行的時候經過new Function('alert(1)')
來構造一個新的函數。優化
// 把函數賦值給變量 let fn = function () { console.log("hi") } fn() // 一個示例 const BlogController = { index (posts) { return Views.index(posts) }, show (post) { return Views.show(post) }, create (attrs) { return Db.create(attrs) }, update (post, attrs) { return Db.update(post, attrs) }, destroy (post) { return Db.destroy(post) } } // 優化 賦值的是Views的index方法,不是方法的調用 const BlogController = { index: Views.index, show: Views.show, create: Db.create, update: Db.update, destroy: Db.destroy }
下面兩個特性在高階函數中會有詳細說明
高階函數(Higher-order function)
// forEach // 定義一個遍歷數組的並對每一項作處理的函數,第一個函數是一個數組,第二個參數是一個函數。 function forEach (array, fn) { for (let i = 0; i < array.length; i++) { fn(array[i]) } } // test let arr = [1, 2, 3] forEach(arr, item => { item = item * 2 console.log(item) // 2 4 6 })
// filter // 遍歷數組,並把知足條件的元素存儲成數組,再進行返回 function filter(array, fn) { let results = [] for (let i = 0; i < array.length; i++) { //若是知足條件 if (fn(array[i])) { results.push(array[i]) } } return results } // test let arr = [1, 3, 4, 7, 8] let result = filter(arr, item => item % 2 === 0) console.log(result) // [4, 8]
// 一個函數返回另外一個函數 function makeFn () { let msg = 'Hello function' return function () { console.log(msg) } } // test // 第一種調用方式 const fn = makeFn() fn() //Hello function // 第二種調用方式 makeFn()()///Hello function
// once // 讓函數只執行一次 function once(fn) { let done = false return function() { // 判斷值有沒有被執行,若是是false表示沒有執行,若是是true表示已經執行過了,沒必要再執行 if(!done) { done = true // 調用fn,當前this直接傳遞過來,第二個參數是把fn的參數傳遞給return的函數 return fn.apply(this, arguments) } } } // test let pay = once(function (money) { console.log(`支付:${money} RMB`) }) pay(5) //支付:5 RMB pay(5) pay(5) pay(5) pay(5)
有一個通用的特色,須要一個函數做爲參數。
const map = (array, fn) => { let results = [] for (const value of array) { results.push(fn(value)) } return results } // test let arr = [1, 2, 3, 4] arr = map(arr, v => v * v) console.log(arr) //
const every = (array, fn) => { let result = true for (const value of array) { result = fn(value) // 若是有一個元素不知足就直接跳出循環 if (!result) { break } } return result } // test let arr = [11, 12, 14] let r = every(arr, v => v > 10) console.log(r) // false r = every(arr, v => v > 12) console.log(r) // false
const some = (array, fn) => { let result = false for (const value of array) { result = fn(value) // 若是有一個元素不知足就直接跳出循環 if (result) { break } } return result } // test let arr = [1, 3, 4, 9] let arr1 = [1, 3, 5, 9] let r = some(arr, v => v % 2 === 0) console.log(r) // true r = some(arr1, v => v % 2 === 0) console.log(r) // false
閉包:函數和其周圍的狀態(詞法環境)的引用捆綁在一塊兒造成閉包
在上面函數做爲返回值的過程當中,其實咱們就用到了閉包,下面進行語法演示:
function makeFn () { let msg = 'Hello function' } // 正常狀況下,執行完makeFn,裏面的變量msg會釋放掉 // 可是下面的狀況 function makeFn () { let msg = 'Hello function' return function () { console.log(msg) } } // 在上面函數中,返回了一個函數,並且在函數中還訪問了原來函數內部的成員,就能夠稱爲閉包 const fn = makeFn() fn() // fn爲外部函數,當外部函數對內部成員有引用的時候,那麼內部的成員msg就不能被釋放。當咱們調用fn的時候,咱們就會訪問到msg。 //注意的點: //一、咱們能夠在另外一個做用域調用makeFn的內部函數 //二、當咱們調用內部函數的時候咱們能夠訪問到內部成員
把函數內部成員的做用範圍延長
函數在執行的時候會放到一個執行棧上,當函數執行完畢以後會從執行棧上移除。可是堆上的做用域成員由於被外部引用不能釋放,所以內部函數依然能夠訪問外部函數的成員。
/解讀:函數執行的時候在執行棧上,執行完畢以後從執行棧上移除,內部成員的內存被釋放。可是在函數執行完畢移除以後,釋放內存的時候,若是外部有引用,則內部成員的內存不能被釋放。/
Math.pow(4, 2) Math.pow(5, 2) // 後面的二次方三次方不少次重複,下面要寫一個二次方三次方的函數 function makePower (power) { return function (number) { return Math.pow(number, power) } } // 求平方 let power2 = makePower(2) let power3 = makePower(3) console.log(power2(4)) // 16 console.log(power2(5)) // 25 console.log(power3(4)) // 64
調試臺的案例演示
// 假設計算員工工資的函數第一個函數傳基本工資,第二個參數傳績效工資 // getSalary(12000, 2000) // getSalary(15000, 3000) // getSalary(15000, 4000) // 不一樣級別的員工基本工資是同樣的,因此咱們將基本工資提取出來,以後只須要加上績效工資 function makeSalary (base) { return function (performance) { return base + performance } } let salaryLevel1 = makeSalary(12000) let salaryLevel2 = makeSalary(15000) console.log(salaryLevel1(2000)) //14000 console.log(salaryLevel2(3000)) //18000 console.log(salaryLevel2(4000)) //19000