JavaScript中的函数克里化的一点理解

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();

有几个问题需要注意:

  1. 调用几个set函数顺序没有约束
  2. invoke中函数调用是的this需要额外处理
  3. 克里化的层数越多,查找最左边的参数的Closure链就越长(不过JS引擎会做优化的

灵活运用

克里化的定义是每次返回的函数的参数都是一个。通过上面OOP的代码我们反推。每个set函数的参数可以是多个。

这样在某个点需要就可以绑定多个参数,不拘泥于『克里化』。当然,通过Object Destructing,可以将任意参数都变为一个参数,实现『真·克里化』。

 

资料:

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top