redux之compose

redux 是狀態管理庫,與其餘框架如 react 是沒有直接關係,因此 redux 能夠脫離 react 在別的環境下使用。因爲沒有和react 相關邏輯耦合,因此 redux 的源碼很純粹,目的就是把如何數據管理好。而真正在 react 項目中使用 redux 時,是須要有一個 react-redux 看成鏈接器,去鏈接 reactreduxhtml

沒看 redux 源碼以前,我以爲看 redux 應該是件很困難的事情,由於當初在學 redux 如何使用的時候就已經被 redux 繁多的概念所淹沒。真正翻看 redux 源碼的時候,會發現 redux 源碼內容至關之少,代碼量也至關少,代碼質量也至關高,因此是很是值得看的源碼。react

目錄結構

其餘目錄均可以不看,直接看 ./src 吧:git

.\REDUX\SRC │ applyMiddleware.js │ bindActionCreators.js │ combineReducers.js │ compose.js │ createStore.js │ index.js │ └─utils actionTypes.js isPlainObject.js warning.js編程

index.js 就是把 applyMiddleware.js 等聚集再統一暴露出去。utils 裏面就放一些輔助函數。因此一共就五個文件須要看,這五個文件也就是 redux 暴露出去的五個 APIredux

// index.js
import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'

// 忽略內容

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes
}
複製代碼

compose.js

這是五個 API 裏惟一一個能單獨拿出來用的函數,就是函數式編程裏經常使用的組合函數,和 redux 自己沒有什麼多大關係,先了解下函數式編程的一些概念:數組

純函數是這樣一種函數,即相同的輸入,永遠會獲得相同的輸出,並且沒有任何可觀察的反作用。 代碼組合app

代碼:框架

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
複製代碼

其實 compose 函數作的事就是把 var a = fn1(fn2(fn3(fn4(x)))) 這種嵌套的調用方式改爲 var a = compose(fn1,fn2,fn3,fn4)(x) 的方式調用。ide

reduxcompose 實現很簡潔,用了數組的 reduce 方法,reduce 的用法能夠參照 mdn函數式編程

核心代碼就一句:return funcs.reduce((a,b) => (..args) => a(b(...args)))

我雖然常常寫 reduce 函數,可是看到這句代碼仍是有點懵的,因此這裏舉一個實際的例子,看看這個函數是怎麼執行的:

import {compose} from 'redux'
let x = 10
function fn1 (x) {return x + 1}
function fn2(x) {return x + 2}
function fn3(x) {return x + 3}
function fn4(x) {return x + 4}

// 假設我這裏想求得這樣的值
let a = fn1(fn2(fn3(fn4(x)))) // 10 + 4 + 3 + 2 + 1 = 20

// 根據compose的功能,咱們能夠把上面的這條式子改爲以下:
let composeFn = compose(fn1, fn2, fn3, fn4)
let b = composeFn(x) // 理論上也應該獲得20
複製代碼

看一下 compose(fn1, fn2, fn3, fn4)根據 compose 的源碼, 其實執行的就是: [fn1,fn2,fn3.fn4].reduce((a, b) => (...args) => a(b(...args)))

第幾輪循環 a的值 b的值 返回的值
第一輪循環 fn1 fn2 (...args) => fn1(fn2(...args))
第二輪循環 (...args) => fn1(fn2(...args)) fn3 (...args) => fn1(fn2(fn3(...args)))
第三輪循環 (...args) => fn1(fn2(fn3(...args))) fn4 (...args) => fn1(fn2(fn3(fn4(...args))))

循環最後的返回值就是 (...args) => fn1(fn2(fn3(fn4(...args))))。因此通過 compose 處理過以後,函數就變成咱們想要的格式了。

總結

compose 函數在函數式編程裏很常見。這裏 redux 的對 compose 實現很簡單,理解起來卻沒有那麼容易,主要仍是由於對 Array.prototype.reduce 函數沒有那麼熟練,其次就是這種接受函數返回函數的寫法,再配上幾個連續的 => ,容易看暈。

這是 redux 解讀的第一篇,後續把幾個 API 都講一下。特別是 applyMiddleware 這個 API 有用到這個 compose 來組合中間件,也是有那麼一個點比較難理解。

相關文章
相關標籤/搜索