# add(1)(2)(3)

# 红宝书第三版的函数柯里化

function curry(fn) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    var innerArgs = Array.prototype.slice.call(arguments);
    var finalArgs = args.concat(innerArgs);
    return fn.apply(null, finalArgs);
  };
}

这个版本的 curry 函数主要是用于创建已经设置好了一个或多个参数的函数。它的使用方式比较有限,是这样的:

function add(a, b) {
  return a + b;
}
var curryAdd1 = curry(add, 1);
console.log(curryAdd1(2)); // 3
var curryAdd2 = curry(add, 1, 2);
console.log(curryAdd2()); // 3

如果要实现add(1)(2)(3),还需要继续改造。

# 实现 add(1)(2)(3)

// 最简单的实现,只支持固定的调用方式
const add = x => y => z => x + y + z;
add(1)(2)(3); // 6

// 升级版,支持更灵活的调用
// add(1, 2, 3);
// add(1, 2)(3);
// add(1)(2, 3);
const curry = function(fn) {
  var args = Array.prototype.slice.call(arguments, 1);
  if (args.length >= fn.length) {
    return fn.apply(null, args);
  } else {
    return function() {
      var innerArgs = Array.prototype.slice.call(arguments);
      return curry(fn, ...args, ...innerArgs);
    };
  }
};
function add(a, b, c) {
  return a + b + c;
}
const curryAdd = curry(add);
console.log(curryAdd(1, 2, 3));
console.log(curryAdd(1)(2)(3));
console.log(curryAdd(1, 2)(3));
console.log(curryAdd(1)(2, 3));
// 考虑以下调用方式改如何实现
// curryAdd(1, 2, 3);
// curryAdd(1)(2)(3);
// curryAdd(1, 2)(3);
// curryAdd(1)(2, 3);

这个版本相对灵活一点,支持更多的调用方式。 下面再写一个支持任意参数的链式调用的方式。

# 链式调用

function argsSum(args) {
  return args.reduce((pre, cur) => {
    return pre + cur;
  });
}
function add(...args1) {
  let sum1 = argsSum(args1);
  let fn = function(...args2) {
    let sum2 = argsSum(args2);
    return add(sum1 + sum2);
  };
  fn.toString = function() {
    return sum1;
  };
  return fn;
}
add(1); // 1
add(1)(2); // 3
add(1)(2)(1)(2)(1)(2)(1)(2); // 12
function curry(fn) {
  const totalArgsNum = fn.length;
  let allArgs = [];
  function newFn(...args) {
    allArgs = allArgs.concat(args);
    if (allArgs.length >= totalArgsNum) {
      const args = allArgs.slice(0, totalArgsNum);
      fn(...args);
      allArgs = [];
      return newFn;
    } else {
      return newFn;
    }
  }
  return newFn;
}

const sum = curry(function _sum(a, b, c) {
  console.log(a + b + c);
});

sum(1)(2)(3); // 6
sum(1, 2)(3); // 6
sum(1)(2, 3); // 6
sum(1, 2, 3); // 6
sum(1, 2, 3, 4); // 6