Compose

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

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
  }
}