Currying

柯里化:把多参函数转换为单参函数(是函数式编程的重要概念)。
思路:获取函数形参长度,每一次单参函数的执行目的是获取并保存参数,待最后一次单参函数执行把之前收集的参数传递给原函数执行。

  1. const curry = fn => {
  2. let length = fn.length
  3. const args = []
  4. return function genFn(arg){
  5. args.push(arg)
  6. if (--length === 0) return fn(...args)
  7. return genFn
  8. }
  9. }
  10. // test case
  11. function sum(a, b, c){
  12. return a + b + c
  13. }
  14. curry(sum)(1)(2)(3)
  15. // curry(sum)(1,2)(3) 报错

但是碰到非全柯里化调用则会报错,稍作修改:非单参调用则根据调用实参个数对应修改,另外使用 apply。

const curry = fn => {
  let length = fn.length
  const args = []
  return function genFn(..._args){
    args.push(..._args)
    if ((length -= _args.length) === 0) return fn.apply(this, args)
    return genFn
  }
}

// test case
function sum(a, b, c){
  return a + b + c 
}

console.log(curry(sum)(1)(2)(3))
console.log(curry(sum)(1,2)(3))
console.log(curry(sum)(1)(2,3))
console.log(curry(sum)(1,2,3))

Uncurrying

反柯里化:可以理解为方法的泛化,使特定对象的方法被指定对象借用,有点 call/apply 的感觉是吧,没错就是利用 call/apply 实现。

Function.prototype.uncurry = function(){
  const fn = this
  return (...args) => {
    Function.prototype.call.apply(fn, args)
    // 可理解成 fn.call(...args)
  }
}

结合用法解释下:

Function.prototype.uncurry = function(){
  const fn = this
  return (...args) => {
    Function.prototype.call.apply(fn, args)
    //     执行下面的 push(obj, 'Sean', 'Messi') 
    //  -> Function.prototype.call.apply(Array.prototype.push, [obj, 'Sean', 'Messi'])
    //  -> Array.prototype.push.call(obj, 'Sean', 'Messi')

  }
}

const push = Array.prototype.push.uncurry()
const obj = {}
push(obj, 'Sean', 'Messi') // 对象借用数组的 push 方法

console.log(obj)
// {0: "Sean", 1: "Messi", length: 2}

参考

Javascript 中有趣的反柯里化技术
Uncurrying “this” in JavaScript
JS进阶篇—JS中的反柯里化( uncurrying)