鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - 实现 Curry 柯里化函数

学习如何实现 JavaScript Curry 柯里化函数,掌握函数式编程中的部分应用与参数收集技巧,提升前端面试竞争力。

影片縮圖

懒得看文章?那就来看视频吧

什么是柯里化 (Currying)?

柯里化是一种将接受多个参数的函数转换成一系列使用一个参数的函数的技术。这是函数式编程中的核心概念,源自数学家 Haskell Curry 的工作。

简单来说,柯里化就是把形如 f(a, b, c) 的函数转换为 f(a)(b)(c) 的形式。

柯里化的好处

  • 1. 参数复用:固定部分参数,创建更专用的函数
  • 2. 延迟执行:收集足够参数前不执行,可以提高性能
  • 3. 提高可读性:使代码更加模块化和可组合
  • 4. 符合函数式编程理念:单一责任原则与关注点分离

基本实现:柯里化函数

最基本的柯里化函数接收一个多参数函数,并返回一系列单参数函数:

// 基本柯里化函数 (固定参数数量)
function curry(fn) {
  // 获取原函数需要的参数个数
  const arity = fn.length;
  
  return function curried(...args) {
    // 如果提供的参数已足够,则直接调用原函数
    if (args.length >= arity) {
      return fn.apply(this, args);
    }
    
    // 否则返回一个新函数,继续收集参数
    return function(...moreArgs) {
      // 合并已有参数和新参数,递归调用
      return curried.apply(this, [...args, ...moreArgs]);
    };
  };
}

使用示例

// 定义一个普通的加法函数
function add(x, y, z) {
  return x + y + z;
}

// 使用柯里化
const curriedAdd = curry(add);

// 多种调用方式 (全部结果都是 6)
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 - 混合提供参数

// 也可以创建偏函数 (Partial Function)
const add1 = curriedAdd(1);          // 固定第一个参数为 1
console.log(add1(2, 3));             // 6

const add1and2 = curriedAdd(1)(2);   // 固定前两个参数为 1 和 2
console.log(add1and2(3));            // 6

实际应用场景

柯里化在实际开发中有许多用途:

1. 事件处理函数

// 事件处理与日志记录
function handleEvent(eventType, element, event) {
  console.log('event triggered on');
  // 处理事件...
}

// 柯里化后
const curriedHandleEvent = curry(handleEvent);

// 创建特定类型的事件处理函数
const handleClick = curriedHandleEvent('click');
const handleKeypress = curriedHandleEvent('keypress');

// 再创建特定元素的事件处理函数
const handleButtonClick = handleClick('button');
const handleInputKeypress = handleKeypress('input');

// 使用
button.addEventListener('click', handleButtonClick);
input.addEventListener('keypress', handleInputKeypress);

2. 函数组合与管道 (Pipeline)

// 数据处理管道
const multiply = curry((x, y) => x * y);
const add = curry((x, y) => x + y);

// 创建特定函数
const double = multiply(2);
const triple = multiply(3);
const addTen = add(10);

// 组合函数
const pipeline = [double, addTen, triple];
const processData = (x) => pipeline.reduce((acc, fn) => fn(acc), x);

console.log(processData(5)); // ((5 * 2) + 10) * 3 = 60