魯斯前端布魯斯前端

文章中英模式

布魯斯前端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