Fullstack / Frontend(多帖) · Technical phone screen 第一轮

Salesforce JS 柯里化:实现 addThreeNumbers(a)(b)(c)

Salesforce Fullstack 面经明确要求把普通加法函数改成逐次调用的 addThreeNumbers(a)(b)(c);本文讲闭包捕获、调用阶段与 JS 加法类型边界。

题目重点:Salesforce Fullstack 面经明确要求把普通加法函数改成逐次调用的 addThreeNumbers(a)(b)(c);本文讲闭包捕获、调用阶段与 JS 加法类型边界。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练版三个参数均为 JavaScript number,最后一次调用返回数值和;前两次调用返回函数。若允许字符串参与,+ 可能拼接,需另定义类型约束或显式转换。
  • 只支持固定三次、每次一个参数的调用形状 a)(b)(c;不要求 addThreeNumbers(1,2,3)、任意长度累加或 this 绑定。
  • 闭包持有已传入参数,直到返回的函数不再被引用;代码不修改外部变量。

解题路线

  1. 把三参数函数分阶段:第一次接收 a 返回第二阶段函数;第二阶段接收 b 返回第三阶段函数;第三阶段接收 c 才计算 a+b+c。内层函数通过词法作用域访问前两层参数。
  2. 正确性可按调用拆解:f1=addThreeNumbers(a) 闭包固定 a;f2=f1(b) 固定 a,b;f2(c) 计算并返回三者之和。每次调用都新建自己的闭包,两个并行调用链不会混用参数。
  3. 如果面试官追问泛化,先区分 currying(固定 arity 转成多次单参数调用)与 partial application(一次预填部分参数);任意参数量/提前终止需要明确结束标志,不能从本题直接推断。

手动走一遍

addThreeNumbers(1)(2)(3) 先得到记住 1 的函数,再得到记住 1、2 的函数,最后返回 6。const addOne=addThreeNumbers(1); addOne(2)(3)=6,addOne(5)(7)=13;两个调用链共享 a=1,但 b 各自独立。

JavaScript 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

export function addThreeNumbers(a) {
  return function addSecond(b) {
    return function addThird(c) {
      return a + b + c;
    };
  };
}

复杂度

调用三阶段时每阶段常数操作,整体时间 O(1);共创建两个返回闭包,辅助空间 O(1),但若调用方长期保存部分应用函数,其捕获参数会随函数一起存活。固定三参时不需要递归或数组累积。

易错点与追问

容易失分的边界

  • 在第一层直接返回 a+b+c,此时 b/c 尚不存在;前两层必须返回函数。
  • 使用一个可变全局 accumulator,会让两个并行调用链互相污染。
  • 忽略 JavaScript + 的字符串拼接语义,却声称任意输入总是数值求和。
  • 把 `addThreeNumbers(1)(2)(3)` 与 `addThreeNumbers(1,2,3)` 当同一调用形状。

继续深挖

  • 若要求支持任意 n 个参数,如何指定 n 或结束调用,避免无限等待下一个参数?
  • 若原函数依赖 this,使用箭头函数或 bind 时会如何改变 this 的捕获?
  • 如何用 TypeScript 为每个阶段标注参数与返回函数类型,拒绝传入字符串?
✓已复制微信号!