type Func<T extends any[], R> = (...a: T) => R
/**
* 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 funcs The functions to compose.
* @returns 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(): <R>(a: R) => R
export default function compose<F extends Function>(f: F): F
/* two functions */
export default function compose<A, T extends any[], R>(
f1: (a: A) => R,
f2: Func<T, A>
): Func<T, R>
/* three functions */
export default function compose<A, B, T extends any[], R>(
f1: (b: B) => R,
f2: (a: A) => B,
f3: Func<T, A>
): Func<T, R>
/* four functions */
export default function compose<A, B, C, T extends any[], R>(
f1: (c: C) => R,
f2: (b: B) => C,
f3: (a: A) => B,
f4: Func<T, A>
): Func<T, R>
/* rest */
export default function compose<R>(
f1: (a: any) => R,
...funcs: Function[]
): (...args: any[]) => R
export default function compose<R>(...funcs: Function[]): (...args: any[]) => R
export default function compose(...funcs: Function[]) {
if (funcs.length === 0) {
// infer the argument type so it is usable in inference down the line
return <T>(arg: T) => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce(
(a, b) =>
(...args: any) =>
a(b(...args))
)
}
compose 将 fn1(fn2(fn3(fn4(x))))
这种嵌套调用改成 compose(fn1, fn2, fn3, fn4)(x)
通过 reduce 不断将右边的函数当成参数传入已组合好的函数中
reselect 的设计初衷是为了解决 react-redux 架构中,在把全局 store 里的 state 树的部分状态映射到对应组件的 props 中(mapStateToProps),每次组件更新的时候都需要执行计算的问题。即便组件依赖的状态并未发生改变
createSelector函数是由一个高阶函数,通过传入存储策略函数,以及缓存过期的比较函数来生成。reselect使用了默认提供的defaultMemoize和引用比较器defaultEqualityCheck来生成createSelector函数
export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
let lastArgs = null
let lastResult = null
return function() {
if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {
// apply arguments instead of spreading for performance.
lastResult = func.apply(null, argument)
}
lastArgs = arguments
return lastResult
}
}
function defaultEqualityCheck(a, b) {
return a === b
}
通过闭包定义了两个私有变量:lastArgs和lastResult,分别代表上一次执行计算所用的参数集合和计算的结果。并在返回的结果计算函数中对最新的参数和上一次的参数进行equalityCheck,如果相同则使用缓存;如果不同则重新计算结果
export function createSelectorCreator(memoize, ...memoizeOptions) {
return (...funcs) => {
//重新计算的次数统计
let recomputations = 0
//弹出结果计算函数
const resultFunc = funcs.pop()
//提取依赖函数数组,并对funcs数组中元素进行校验,默认只支持function
const dependencies = getDependencies(funcs)
//对结果计算函数使用用户传入的缓存策略(默认为上面说的defaultMemoize)。并且每执行一次结果计算函数,计数就加1
const memoizedResultFunc = memoize(
function () {
recomputations++
// apply arguments instead of spreading for performance.
return resultFunc.apply(null, arguments)
},
...memoizeOptions
)
//selector就是createSelector的返回值函数。
//selector函数要做的事情就是把依赖的每一个中间结果计算函数依次执行,并组装成一个结果数组。
//交给结果计算函数处理。在selector函数的参数值引用未发生变化时,中间计算函数不需要重复进行计算。
const selector = defaultMemoize(function () {
const params = []
const length = dependencies.length
for (let i = 0; i < length; i++) {
// 循环执行所有依赖的计算函数,并把执行的结果保存在数组缓存中
params.push(dependencies[i].apply(null, arguments))
}
//执行结果计算函数,返回最终计算结果
return memoizedResultFunc.apply(null, params)
})
//绑定结果处理函数
selector.resultFunc = resultFunc
selector.recomputations = () => recomputations
selector.resetRecomputations = () => recomputations = 0
return selector
}
}