柯里化 vs 偏函数

333 阅读3分钟

柯里化

🌰 1. 柯里化意义

柯里化(Currying)本质就是把一个多参数函数拆成若干个一参函数链式调用,即把 fn(a, b, c) 变成 fn(a)(b)(c) 的技术。

Currying 指将一个接收多个参数的函数,转换成一系列接收单一参数的函数,并且返回一个新的函数,逐步接收并收集参数,直到参数足够后再执行原函数。

🌟 2. 柯里化作用

柯里化不是为了装逼,它的核心价值主要有三:

① 复用参数(函数预置参数)

const add = (a, b) => a + b;
const add5 = curry(add)(5);

add5(10) // 15

② 提高函数组合能力

const match = curry((reg, str) => reg.test(str));
const isPhone = match(/^1\d{10}$/);

isPhone("13812345678"); // true

③ 延迟执行(参数收集满了再执行)

例如防抖节流里参数不固定时很好用

🔧 3. 实现柯里化

👉 固定参数长度

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return (...rest) => curried(...args, ...rest);
  };
}

测试一下:

const sum = (a, b, c) => a + b + c;

curry(sum)(1)(2)(3);   // 6
curry(sum)(1, 2)(3);   // 6
curry(sum)(1)(2, 3);   // 6

🧠 不固定参数长度

比如:

add(1)(2)(3)(4).valueOf() === 10

或:

add(1)(2)(3)() === 6

实现思路:

  • 不固定参数长度
  • 不停收集
  • 重写 toString / valueOf / [Symbol.toPrimitive]

示例:

function add(...args) {
  let total = args.reduce((s, v) => s + v, 0);

  function inner(...rest) {
    if (rest.length === 0) return total;
    total += rest.reduce((s, v) => s + v, 0);
    return inner;
  }

  inner.toString = () => total;
  inner.valueOf = () => total;

  return inner;
}

add(1)(2)(3)()     // 6
+add(1)(2)(3)(4)   // 10

🎯 柯里化应用

① 业务逻辑复用

const validate = curry((rule, value) => rule.test(value));

const isEmail = validate(/@/);
const isPhone = validate(/^1\d{10}$/);

② 事件绑定(偏应用)

const handleChange = curry((field, e) => {
  form[field] = e.target.value;
});

<input onChange={handleChange("username")} />

③ Redux / Ramda 里大量使用

const map = curry((fn, arr) => arr.map(fn));

偏函数

偏函数意义

偏函数就是把原函数的部分参数固定住,返回一个新的函数,等以后再补剩下的参数。

它的重点是:

  • 一次可以固定多个参数(而不是一个一个固定)
  • 参数什么时候补都行,不需要拆成一参链式函数
  • 它不是柯里化,但作用类似:抽离复用、提前绑定参数

原函数:

function sum(a, b, c) {
  return a + b + c;
}

偏函数应用:

const add10 = partial(sum, 10);  // 固定第一个参数
add10(2, 3) // 15

偏函数相当于提前把 a=10 固定了

⚡ 实现偏函数

最简单易背的版本:

function partial(fn, ...fixedArgs) {
  return function (...restArgs) {
    return fn(...fixedArgs, ...restArgs);
  };
}

用法:

const multiply = (a, b, c) => a * b * c;

const double = partial(multiply, 2); 
double(3, 4); // 24

一次固定多个参数:

const times6 = partial(multiply, 2, 3);
times6(4); // 24

🧠 偏函数应用

① 固定 API 参数(常用)

function request(method) {
  return (url, data) => fetch(url, { method, body: data });
}

const get = request('GET');
const post = request('POST');

get('/users');
post('/users', { name: '二二四一同志' });

② 日志函数预绑定前缀(场景经典)

const log = partial(console.log, '[MyApp]');
log('启动成功'); 
// => [MyApp] 启动成功

③ UI 事件绑定

function handleChange(field, value) {
  form[field] = value;
}

const updateUsername = partial(handleChange, "username");
updateUsername("Alice");

④ React 中高频用法(绑定参数)

<button onClick={partial(handleClick, item.id)}>删除</button>

偏函数就是对一个函数预先绑定部分参数,返回一个新的“定制化”函数。偏函数一次可以固定多个参数,补齐剩下的参数后执行原函数。

🔥 偏函数 vs 柯里化

特性柯里化(Currying)偏函数(Partial)
参数处理把函数拆成一参链式函数预先固定部分参数
调用方式f(a)(b)(c)f(a, b)(c)g(c)
参数收集规则逐步收集,每次只能给一个(经典定义)一次可以给多个剩余参数
适用场景函数组合、函数式编程预绑定部分参数、构造专用函数
学术地位理论较重(λ calculus)更实用更业务化

偏函数是固定一部分参数;柯里化是拆分成一参函数链式调用。二者都能实现参数预置,但机制完全不同。