函數式編程(一)—— 前置知識

  • 爲何要學函數式編程?
  • 什麼是函數式編程?編程

    • 函數式編程和麪向對象編程的不一樣
    • 對於函數式編程思惟方式的理解:
  • 函數式編程的前置知識segmentfault

    • 函數是一等公民
    • 高階函數數組

      • 什麼是高階函數?
      • 使用高階函數的意義
      • 經常使用的高階函數
    • 閉包閉包

      • 閉包的概念
      • 閉包的核心做用
      • 閉包的本質
      • 閉包的案例app

        • 案例一
        • 案例二
  • 【函數式編程整體設計】
ps:對總體不太瞭解的先看有線函數式編程整體設計,再閱讀下面的內容~~~

爲何要學函數式編程?

函數式編程是一個很是古老的概念。函數式編程

  • 函數式表成是隨着React的流行收到愈來愈多的關注(React的高階組件使用了高階函數來實現,高階函數就是函數式編程的一個特性。Redux也使用了函數式編程的思想。)
  • Vue3也開始擁抱函數式編程
  • 函數式編程能夠拋棄this
  • 打包過程當中能夠更好的利用tree shaking過濾無用代碼
  • 方便測試、方便並行處理
  • 有不少庫能夠幫助咱們進行函數式開發:lodash、underscore、ramda

什麼是函數式編程?

函數式編程,縮寫FP,是一種編程範式,也是一種編程風格,和麪向對象是並列的關係。函數式編程咱們能夠認爲是一種思惟模式,加上實現方法。其思惟方式就是把現實世界事物和事物之間的聯繫抽象到程序世界(是對運算過程進行抽象函數

常據說的編程範式還有面向過程編程(按照步驟來實現)、面向對象編程(把現實中的事物抽象成類和對象,經過封裝、繼承和多態來演示不一樣事物之間的聯繫)post

函數式編程和麪向對象編程的不一樣

從思惟方式上來講 面向對象編程是對事物的抽象,而函數式編程是對運算過程的抽象測試

對於函數式編程思惟方式的理解:

  • 程序的本質:根據輸入經過某種運算得到相應的輸出,程序開發過程當中會涉及不少輸入和輸出的函數。
  • 函數式編程中的函數指的不是程序中的函數Function,而是數學中的函數即映射關係,例如:y=sin(x),是這種x和y的關係
  • 相同的輸入時鐘要獲得相同的輸出(純函數)
  • 函數式編程用描述數據(函數)之間的映射
// 非函數式
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)

使用高階函數的意義

  • 抽象能夠幫咱們屏蔽細節,咱們只須要知道咱們的目標和解決這類問題的函數,咱們不須要關心實現的細節
  • 高階函數是用來抽象通用的問題

經常使用的高階函數

有一個通用的特色,須要一個函數做爲參數。

  • forEach
  • map 對數組中的每一個元素進行遍歷,並處理,處理的結果放在一個新數組中返回
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)
//
  • filter
  • every 數組中的每個元素是否都匹配咱們指定的一個條件,若是都知足返回true,若是不知足返回false
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
  • some 判斷數組中是否有一個元素知足咱們指定的條件,知足是true,都不知足爲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
  • find/findIndex
  • reduce
  • sort

閉包

閉包的概念

閉包:函數和其周圍的狀態(詞法環境)的引用捆綁在一塊兒造成閉包

  • 通俗的講:能夠在另外一個做用域中調用一個函數的內部函數並訪問到該函數做用域中的成員

在上面函數做爲返回值的過程當中,其實咱們就用到了閉包,下面進行語法演示:

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

函數式編程整體設計

image

相關文章
相關標籤/搜索