redux原始碼閱讀之compose,applyMiddleware
我的觀點是,看別人的原始碼,不追求一定要能原樣造輪子,單純就是學習知識,對於程式設計師的提高就足夠了。在閱讀redux的compose原始碼之前,我們先學一些前置的知識。
redux原始碼閱讀之compose,applyMiddleware
看別人的原始碼就是學習知識。我們先學一些東西。
rest引數
形式為...變數名,用於獲取函式的多餘引數 ,該變數將多餘的引數放入陣列中, 只能是引數的最後一個。
function rest(...args){ console.log(args); }; rest('arg1','arg2')
那麼args變數就是一個數組['arg1','arg2']。
擴充套件運算子
擴充套件運算子也是三個點。好比rest引數的逆運算,將一個數組轉為用逗號分隔的引數序列
arr.reduce(callback[initialValue])
個人感覺reduce用法非常魔幻,要搞明白它之後,才能明白compose。
MDN的reduce文件:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
使用
const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // 10 // 5 + 1 + 2 + 3 + 4 console.log(array1.reduce(reducer, 5)); // 15 array1.reduce((accumulator, currentValue) => accumulator + currentValue,5)
根據MDN的定義,
陣列的reduce() 方法對陣列中的每個元素執行一個由您提供的reducer函式(升序執行),將其結果彙總為單個返回值。
arr.reduce(callback[initialValue])
實際就是arr.reduce(callback, initialValue);
callback 函式接收4個引數:
Accumulator (acc) (累加器)
Current Value (cur) (當前值)
Current Index (idx) (當前索引)
Source Array (src) (源陣列)
initialValue 可選
作為第一次呼叫 callback函式時的第一個引數的值。 如果沒有提供初始值,則將使用陣列中的第一個元素。 不傳這個初始值initialVale,且在空陣列上呼叫 reduce 將報 Uncaught TypeError: Reduce of empty array with no initial value的錯誤。
pip
研究compose之前,我們再來看MDN上reduce的應用,一個功能型函式管道。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#%E5%8A%9F%E8%83%BD%E5%9E%8B%E5%87%BD%E6%95%B0%E7%AE%A1%E9%81%93
類似的我們來寫一個。
const pipe = (...fns) =>
(args) => fns.reduce(
(args, fn) => fn(args)
,args)
吐槽一下,就是這種es6寫法,完全不寫return,很多es5過來的人不習慣,所以很多原始碼很難看清楚。我們改寫一下,傳入引數字串‘test’,然後執行這個函式。就能明白pip函式的作用就是順序執行了addFont,toUpper。
args就是'test',...fns就是rest引數,pip函式裡的fns引數就是陣列[addfont,toUpper]。
const pipe = (...fns) => {
return (args) => {
return fns.reduce((args, fn) => {
return fn(args)
}, args)
}
}
const toUpper = (value) => {
return value.toUpperCase();
}
const addFont = (value) => {
return 'hello plus!' + value;
}
console.log(pipe(addFont,toUpper)('test'));
// HELLO PLUS!TEST
pipe(addFont,toUpper)
之後,返回一個函式.
pipe(addFont,toUpper)('test')
形如
(args) => {
return fns.reduce((args, fn) => {
return fn(args)
}, args)
}('test');
再執行就是
fns.reduce(('test',fn)=>{
return fn('test');
},'test')
這樣子看程式碼,這裡的reduce應該似乎比較好懂的。
compose
compose原始碼
/**
* Composes single-argument functions from right to left. The rightmost
* function can take multiple arguments as it provides the signature for
* the resulting composite function.
*
* @param {...Function} funcs The functions to compose.
* @returns {Function} A function obtained by composing the argument functions
* from right to left. For example, compose(f, g, h) is identical to doing
* (...args) => f(g(h(...args))).
*/
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)))
}
跟pip函式對比
console.log(pipe(addFont,toUpper)('test'));
// HELLO PLUS!TEST
console.log(compose(addFont,toUpper)('test'));
// hello plus!TEST
compose正好相反,後新增的方法先執行。然後寫的也很玄學讓人費解。讓我們改成pip函式一樣,也是同樣的效果,感覺這樣子就跟上面的pip一樣好理解一些了呢。
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return (...funcs) => (...args) => funcs.reduce((a, b) => a(b(...args)))
}
console.log(compose(addFont,toUpper)('test'));
// hello plus!TEST
applyMiddleware
我們再來看redux引入中介軟體的用法,
const store = createStore(
reducer,
applyMiddleware(...middleware)
);
以及applyMiddleware方法原始碼
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
實際上所謂redux的中介軟體就是改寫了store.dipatch方法。比如我們自己實現一個實現一個列印action的type的redux中介軟體,我們就可以這樣寫。
./redux-middlewares/log.js
const logger = ()=>{
return ({dispatch,getState}) => next => action =>{
console.log('dispatching', action);
return next(action);
}
};
export default logger;
跟別的redux中介軟體一起引入
import createSaga from 'redux-saga';
import createLog from './redux-middlewares/log';
const saga = createSaga();
const log = createLog();
const middlewares = [saga,log]
const store = createStore(
rootReducer,
applyMiddleware(...middlewares)
);
store.dispatch({type:'myAction'});
// 列印 dispatching {type: "myAction"}
這裡
redux的新增中介軟體
applyMiddleware(...middleware)
就等於這裡的
dispatch = compose(...chain)(store.dispatch)
.
跟我們實行
compose(addFont,toUpper)('test')
是一樣的效果。
參考文章:
https://www.cnblogs.com/cloud-/p/7282188.html