面试官 : “ 请问你实际开发中用过 函数柯理化 吗? 能讲一下吗 ?”

0 阅读3分钟

一、先搞懂:柯里化到底是什么?

核心定义:柯里化是把接收多个参数的函数,转换成一系列只接收单个参数的函数,并持续返回新函数,直到所有参数都被传入后,才执行最终逻辑并返回结果。

用 “人话” 说:原本要一次性传完所有参数的函数,现在可以 “分批传”,传一个参数就返回一个新函数等着接下一个,直到传完为止。

对比:普通函数 vs 柯里化函数

// 普通函数:一次性传所有参数
function add(a, b, c) {
  return a + b + c;
}
add(1, 2, 3); // 6

// 柯里化函数:分批次传参数
function curriedAdd(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}
curriedAdd(1)(2)(3); // 6(传一个参数,返回新函数,直到传完3个)

二、手动实现一个通用柯里化函数

你不用为每个函数单独写柯里化逻辑,这里写一个通用的 curry 工具函数,能把任意多参数函数转换成柯里化函数:

// 通用柯里化函数
function curry(fn) {
  // 保存原函数的参数个数
  const argsLength = fn.length;
  
  // 递归接收参数
  function curried(...args) {
    // 1. 如果已传参数 >= 原函数需要的参数,执行原函数
    if (args.length >= argsLength) {
      return fn.apply(this, args);
    }
    // 2. 否则,返回新函数,继续接收参数
    return function(...newArgs) {
      return curried.apply(this, [...args, ...newArgs]);
    };
  }
  
  return curried;
}

// 测试:给加法函数做柯里化
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);

// 支持多种传参方式(核心优势)
console.log(curriedAdd(1)(2)(3)); // 6(逐个传)
console.log(curriedAdd(1, 2)(3)); // 6(分批传)
console.log(curriedAdd(1)(2, 3)); // 6(混合传)
console.log(curriedAdd(1, 2, 3)); // 6(一次性传)

三、柯里化的核心价值(为什么要用?)

  1. 参数复用:提前固定部分参数,生成新函数,避免重复传参。示例:固定 “税率” 参数,复用计算逻辑

    // 原函数:计算税后价格(价格 + 税率)
    const calculateTax = (taxRate, price) => price * (1 + taxRate);
    // 柯里化后,固定税率为10%
    const calculateTax10 = curry(calculateTax)(0.1);
    // 后续只用传价格,不用重复传税率
    calculateTax10(100); // 110
    calculateTax10(200); // 220
    
  2. 延迟执行:先收集参数,不立即执行,等参数凑齐后再执行。示例:表单提交前收集多个字段,凑齐后再验证提交

    const submitForm = (name, phone, address) => {
      console.log(`提交:${name} ${phone} ${address}`);
    };
    const curriedSubmit = curry(submitForm);
    
    // 分步收集参数(比如用户分步填写表单)
    const step1 = curriedSubmit("张三"); // 收集姓名,未执行
    const step2 = step1("13800138000"); // 收集手机号,未执行
    step2("北京市"); // 收集地址,参数凑齐,执行 → 输出:提交:张三 13800138000 北京市
    
  3. 适配函数参数:把多参数函数转换成单参数函数,适配只接收单参数的场景(比如 React 的高阶组件、数组的 map/filter 等)。示例:适配数组 map 的单参数回调

    // 原函数:乘以指定倍数
    const multiply = (multiplier, num) => num * multiplier;
    const curriedMultiply = curry(multiply);
    
    // 固定倍数为2,生成单参数函数
    const double = curriedMultiply(2);
    // 适配 map 的单参数回调
    [1,2,3].map(double); // [2,4,6]
    

四、常见误区

❌ 误区:“柯里化就是把函数拆成只传一个参数的函数,必须链式调用 (a)(b)(c)”

✅ 纠正:柯里化的核心是 “参数分批传递 + 延迟执行”,支持任意分批方式(比如 (a,b)(c)、(a)(b,c)),不一定非要逐个传。

❌ 误区:“柯里化能提升性能”

✅ 纠正:柯里化本质是多了层函数嵌套,性能略有损耗,它的价值是提升代码复用性和可读性,而非性能。

总结

  1. 柯里化核心:把多参数函数转成 “单参数函数链”,支持参数分批传递,凑齐后执行;
  2. 实现关键:通过闭包保存已传参数,递归判断参数是否凑齐,凑齐则执行原函数;
  3. 核心用途:参数复用、延迟执行、适配单参数场景。