函數式編程

前言

如今主流的兩種方式,一種是 OOP (Object-oriented programming ) 面向對象編程,另外一種是 FP (Functional Programming)。本文稍微普及一下 FP 這種方式。javascript

特色

與其餘編程範式相比,FP的主要區別在於聲明性方法(FP)與命令式方法。在咱們深刻了解正式定義以前,讓咱們經過查看示例來探索差別。java

🌰聲明式寫法

// 數組每一個元素乘三
const triple = (arr) => {
 let results = []
 for (let i = 0; i < arr.length; i++){
   results.push(arr[i] * 3)
 }
 return results
}

// 數組求和
const sum = (arr) => {
 let result = 0
 for (let i = 0; i < arr.length; i++){
   result += arr[i]
 }
 return result
}
複製代碼
  • 這段代碼片斷的主要複雜性源於這樣一個事實:咱們不是告訴計算機咱們想要它作某些事而是指示如何作到這一點
  • 並且可讀性不好(😱😱😱), 這是一個簡單示例,但隨着程序的增加和功能變得更加複雜,使用像這樣的for循環會建立很是簡單的代碼,而且須要咱們的大腦分析循環的內部工做,同時跟蹤索引,變量和更多。命令式代碼在閱讀時會增長認知負荷,而且隨着時間的推移會使推理和邏輯更容易出錯。

命令式寫法

// 第一個例子
const  triple  =(arr)=>  arr.map((currentItem)=> currentItem *  3)

//第二個例子
const  sum  =(arr)=>  arr.redeuce((prev,current)=> prev + current,0)
複製代碼
  • 首先,我保證在給定相同輸入的狀況下,這兩種方法每次都會產生相同的輸出
  • 很明顯,聲明性片斷比命令式片斷更簡,它也更容易閱讀。在咱們的例子中是一個匿名函數,它告訴程序我但願它對數組中的每一個元素作什麼,而不是指示程序我但願它訪問哪些索引等等

函數做爲一等公民

所謂"第一等公民"(first class),指的是函數與其餘數據類型同樣,處於平等地位,能夠賦值給其餘變量,也能夠做爲參數,傳入另外一個函數,或者做爲別的函數的返回值。react

// 好比咱們要先請求然數據而後渲染模板,可能會這麼寫
const render = (json) => {}
httpGet('/post/2', json => render(json))
複製代碼

若是這個時候咱們除了拿數據還要拿 error 錯誤信息怎麼辦,你可能會想再加個參數編程

const render = (json, err) => {}
httpGet('/post/2', (json, err) => render(json, err))
複製代碼

可是之後若是可還有其餘參數怎麼辦??若是用一等公民的寫法這樣json

const render = (json, err) => {}
httpGet('/post/2', render)
複製代碼

純函數

純的意思是引用透明沒有任何反作用 更多概念性的東西能夠參考阮一峯大大的入門教程數組

// 不純的
const minimum = 21
const checkAge = age => {
 return age  >= minimum
}
// 純的
const checkAge = age => {
 const minimum = 21
 return age  >= minimum
}
複製代碼

再看下引用類型:閉包

const data = [1, 2, 3]
const a = data.slice(0, 1)
// [1]
const b = data.slice(0, 1)
// [1]
const c = data.slice(0, 1)
// 此處是爲了對比因此用了const,正常確定會報錯的
// [1]
const a = data.splice(0, 1)
// [1]
const b = data.splice(0, 1)
// [2]
const c = data.splice(0, 1)
複製代碼

柯里化 Curry

函數柯⾥裏里化就是隻傳遞給函數⼀一部分參數來調⽤用它,讓它返回⼀一個函數去處 理理剩下的參數函數式編程

舉個例子函數

function add (x, y) {
  return x + y
}
function add (x) {
  return function (y) {
return x + y }
}
const add = x => y => x + y
// add(2,3) 等價於 add(2)(3)
//舒服了
複製代碼

這裏咱們定義了一個 add 函數,它接受一個參數並返回一個新的函數。調用 add 以後,返回的函數就經過閉包的方式記住了 add 的第一個參數。一次性地調用它實在是有點繁瑣,好在咱們可使用三方函數式編程庫一個特殊的 curry 幫助函數使這類函數的定義和調用更加容易,後面我會把函數式編程庫不錯的列出來post

代碼組合 Compose

const b = f(a)
const c = g(b)

不用函數式:const c = g(f(a))
用了函數式:const c = compose(g, f)(a)

吃我個🌰

const data = [1, 2, 3]
const head = arr => arr[0]
const reverse = arr =>
  arr.reduce((acc, item) => [item].concat(acc), [])
const last = compose(
  head,
reverse
)
last(data) // 3
 
複製代碼

這裏只是舉個例子取數組最後的一項,真正寫起來不會這麼麻煩那麼我接下來舉個你們用的比較多對象判空的例子

const user = {
    name: 'cai',
    address: {
        city: 'hangzhou'
    }
}
const city =
 user &&
 user.address  &&
 user.address.city
 // 或者這樣
 const city = !user
? undefined
: !user.address
? undefined
: user.address.city
複製代碼

用了函數式之後

const prop = key => obj  => (obj   === undefined  || obj   === null)
  ? null
: obj[key]
const getUserCity = compose(
  prop('city'),
  prop('address')
)
getUserCity(user)  // hangzhou
複製代碼

用對象的形式

class Maybe {
  constructor(val) {
    this. __val = val
  }
  static of(val) {
    return new Maybe(val)
  }
  isNothing() {
    return (this. __val   === null  || this. __val   === undefined)
  }
  map(f) {
    return (
      this.isNothing()
        ? new Maybe(null)
        : new Maybe(f(this. __val))
) }
}
/* 調用 */
// props :: s  -> {s: a}  -> a
const prop = key  => obj  => obj[key]
Maybe
  .of(user)
  .map(prop('address'))
  .map(prop('city'))
 // Maybe {  __val: 'hangzhou' }
複製代碼

題外話

用過 react 童鞋值到 react 有 hoc (高階組件)的概念 其實也是把組件當成參數的形式傳進來返回一個新的組件這點是共通的

推薦

  • ramda、lodash/fp :函數式編程庫
  • Recompose:React 的 Lodash
  • RxJS: 用來處理理事件的 lodash

QA

我的理解系的很差望指證

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息