JavaScript 的秘密生活:Currying vs. Partial Application

发布: (2025年12月15日 GMT+8 12:13)
5 min read
原文: Dev.to

Source: Dev.to

实用方法:部分应用(Partial Application)

// A standard, multi‑argument function
function multiply(a, b) {
    return a * b;
}

// Manually creating partial application via a closure factory
function createMultiplier(factor) {
    // We "lock in" the 'factor' argument here
    return function (number) {
        return number * factor;
    };
}

// Usage
const double = createMultiplier(2); // We applied '2' to the first spot
console.log(double(10)); // 20

部分应用是一种 手动 技巧:你预先填入通用函数的某些参数,生成一个等待剩余参数的新函数。

Original Function: [ Slot A ] [ Slot B ] [ Slot C ]

Partial Application:
We choose to fill Slot A now.
Resulting Function: [ 10 ] [ Slot B ] [ Slot C ]

结构化方法:柯里化(Currying)

柯里化是对函数签名的 结构性转换。它把多参数函数转换为一系列一元(单参数)函数的链式调用。

const formatUrl = protocol => domain => path => `${protocol}://${domain}/${path}`;
// Partial Application – manual process
// We can fill any arguments we want, in any order we design
const addFive = (b, c) => add(5, b, c);

// Currying – structural transformation
// It forces a rigid chain: one arg at a time
const curriedAdd = a => b => c => a + b + c;

// You MUST call it like this:
curriedAdd(1)(2)(3);

尝试打破链式调用会失败:

// This won't work as expected with a truly curried function:
// It returns a function expecting 'path', it does NOT use 'myblog.com' as the domain yet!
formatUrl('https', 'myblog.com');

类比:三明治制作师 vs. 装配线

部分应用的三明治制作师

你有一个通用的三明治配方,需要 面包奶酪。如果你已经知道今天的面包是酸面包,就提前把酸面包片准备好。你手动“部分应用”了面包这个参数,使后续步骤更快。

柯里化的装配线

想象一个拥有三个工作站的工业三明治机器人:

  1. 工作站 1 – 只接受 面包,并把结果传递给工作站 2。
  2. 工作站 2 – 只接受 ,并把结果传递给工作站 3。
  3. 工作站 3 – 只接受 奶酪

你不能同时把面包和肉喂给工作站 1;装配线强制一次只接受一个参数的严格流程。

为什么库里使用柯里化?

因为柯里化可以轻松创建专用的构造器:

// Create a builder specifically for secure sites
const secureUrl = formatUrl('https');

// Create a builder specifically for OUR site
const myBlogUrl = secureUrl('myblog.com');

// Generating links is clean:
const post1 = myBlogUrl('post-1');
const post2 = myBlogUrl('post-2');

柯里化的 formatUrl 让你可以在任意工作站停下来,生成可复用的部分函数,而无需编写新的定义。

常见陷阱:混合模式

// Not truly curried (breaks the single‑argument chain)
const add = a => (b, c) => a + b + c; // returns a function taking TWO args

// Truly curried (maintains single‑argument chain)
const curriedAdd = a => b => c => a + b + c; // each returns a function taking ONE arg

第一个例子是混合式的;真正的柯里化在整个链中保持“一函数一参数”的模式。

Margaret 的速查表

何时使用部分应用:

  • 编写标准的 JavaScript。
  • 为特定用例专门化函数(例如 createMultiplier)。
  • 需要灵活性(例如同时固定第一个 第三个参数,而保持第二个参数开放)。

何时使用柯里化:

  • 使用函数式库(例如 Ramda、Lodash/fp)。
  • 需要最大程度的可组合性(通过管道将数据传递)。
  • 想自动生成大量专用版本的函数。

在日常的 JavaScript 开发中,部分应用出现得更频繁,而真正的柯里化则相对少见——但理解两者的区别有助于你在不同场景下选用合适的工具。

Back to Blog

相关文章

阅读更多 »

JavaScript 中的一等函数

介绍 对于学习 JavaScript 的开发者来说,术语 first‑class functions 在讨论和文档中经常出现。在 JavaScript 中,函数 a...

JavaScript 中的函数组合

介绍 函数组合(functional composition),也称为函数管道(function pipelines),让你能够将简单函数链式连接,以创建更易读且模块化的代码。定义…