# ❓ 请实现一个 add 函数,满足以下功能。

add(1); // 1

add(1)(2); // 3

add(1)(2)(3); // 6

add(1)(2, 3); // 6

add(1, 2)(3); // 6

add(1, 2, 3); // 6

# Coding

// // 这种写法只能满足前三个例子
// function add(a) {
//   // 这里其实用了闭包
//   function sum(b) {
//     a = a + b
//     return sum
//   }

//   // console.log 回调用toString方法
//   sum.toString = () => a

//   return sum
// }

// 函数柯里化
function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      return func.apply(this, args)
    } else {
      return function pass(...args2) {
        return curried.apply(this, args.concat(args2))
      }
    }
  }
}

// 箭头函数
// const curry = (func) =>
//   (curried = (...args) =>
//     args.length >= func.length
//       ? func(...args)
//       : (...args2) => curried(...args, ...args2))

function _add(a, b, c) {
  return a + b + c
}

let add = currying(_add)

console.log('add(1)', add(1))

console.log('add(1)(2)', add(1)(2))

console.log('add(1)(2)(3)', add(1)(2)(3))

console.log('add(1)(2, 3)', add(1)(2, 3))

console.log('add(1, 2)(3)', add(1, 2)(3))

console.log('add(1, 2, 3)', add(1, 2, 3))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 解析

function curry(func) {
  // func 要转换的函数
  return function curried(...args) {
    if (args.length >= func.length) {
      // (1)
      return func.apply(this, args)
    } else {
      return function pass(...args2) {
        // (2)
        return curried.apply(this, args.concat(args2))
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

当我们运行它时,这里有两个 if 执行分支:

  1. 现在调用:如果传入的 args 长度与原始函数所定义的(func.length)相同或者更长,那么只需要将调用传递给它即可。

  2. 获取一个偏函数:否则,func 还没有被调用。取而代之的是,返回另一个包装器 pass,它将重新应用 curried,将之前传入的参数与新的参数一起传入。然后,在一个新的调用中,再次,我们将获得一个新的偏函数(如果参数不足的话),或者最终的结果。