In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. — Wikipedia
一个简单的例子
上面标出了abcd四处调用。
a:直接传入参数调用console.log
b:返回一个函数,其中参数a已经通过闭包绑定为1
c:返回一个函数,其中参数a、b已经通过闭包绑定为1、2
d:调用console.log,a、b、c都已经绑定
换一种写法
换一种写法,就容易理解了。我们加上花括号和return语句,并写在多行里。
结合右侧Scope中的Closure+Local信息,这不就是多个嵌套闭包么。
我们在最里层返回一个function而非arrow function。这样就可以通过call/apply来改变this了。
理解、记忆
普通函数需要在代码的某个地方调用。而克里化的函数可以被传递到不同的地方。在不同的点绑定不同的参数。在最终点进行调用。所以在阅读克里化函数相关代码时就需要找到对应的几个调用点。
redux-thunk
In computer programming, a thunk is a subroutine used to inject an additional calculation into another subroutine. Thunks are primarily used to delay a calculation until its result is needed, or to insert operations at the beginning or end of the other subroutine. It can simply be thought of as a function that takes no arguments, waiting to be called upon to do its work. They have a variety of other applications in compiler code generation and modular programming. — Wikipedia
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
注意action(dispatch, getState, extraArgument)
。就是这一行使得dispatch的第一个参数可以是一个async的函数。
用OOP方式实现
class thunkMiddleware { constructor (extraArgument) { this.extraArgument = extraArgument; } // step1 setDispatchFnAndGetStateFn ({dispatch, setState}) { this.dispatch = dispatch; this.setState = setState; } // step2 setNextFn (next) { this.next = next; } // step3 setActionFn (action) { this.action = action; } // final step invoke () { if (typeof this.action === 'function') { return this.action(this.dispatch, this.getState, this.extraArgument); } return this.next(action); } } var m = new thunkMiddleware(extraArgument); m.setDispatchFnAndGetStateFn({}); m.setNextFn(() => {}); m.setActionFn(() => {}); m.invoke();
有几个问题需要注意:
- 调用几个set函数顺序没有约束
- invoke中函数调用是的this需要额外处理
- 克里化的层数越多,查找最左边的参数的Closure链就越长(不过JS引擎会做优化的)
灵活运用
克里化的定义是每次返回的函数的参数都是一个。通过上面OOP的代码我们反推。每个set函数的参数可以是多个。
这样在某个点需要就可以绑定多个参数,不拘泥于『克里化』。当然,通过Object Destructing,可以将任意参数都变为一个参数,实现『真·克里化』。
资料:
- lodash#curry
- Currying
- https://github.com/dominictarr/curry