前端JS: 函数柯里化

0 阅读3分钟

函数柯里化详解

一、柯里化基本概念

柯里化(Currying) ​ 是一种函数转换技术,它将一个接收多个参数的函数转换为一系列接收单个参数的函数,并返回接收余下参数的新函数。

基本数学原理

普通函数:f(a, b, c)

柯里化后:f(a)(b)(c)

二、实现原理与核心代码

我们正常情况下写sum函数是

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

// 调用
sum(1,2)

柯里化后

const sum = function(a) {
  return function(b) {
    return a + b;
  }
}
// 调用
sum(1)(2)
闭包相当于 ⬇️
function(b) {
    return a + b;
  }(2)


解决N个参数的问题,使用递归函数。

为了解决将所有的函数变成柯里化,所以我们的入参要加上fn


function currying(fn, length) {
    // 第一次调用获取函数 fn 参数的长度,后续调用获取 fn 剩余参数的长度
    length = length || fn.length; 	
    // currying 包裹之后返回一个新函数,接收参数为 ...args
    return function (...args) {			
      // 新函数接收的参数长度是否大于等于 fn 剩余参数需要接收的长度
      return args.length >= length	
          ? fn.apply(this, args) // 满足要求,执行 fn 函数,传入新函数的参数
        : currying(fn.bind(this, ...args), length - args.length) 
      // 不满足要求,递归 currying 函数,新的 fn 为 bind 返回的新函数
      //(bind 绑定了 ...args 参数,未执行),新的 length 为 fn 剩余参数的长度
    }
  }
  
  const sum = function(a,b,c){
    return a + b + c;
  }

const currySum = currying(sum);

console.log(currySum(1,2,3));
console.log(currySum(1)(2)(3));
console.log(currySum(1,2)(3));
// 输出都是 6 

二、实际应用场景

1. 参数复用

javascript
javascript
下载
复制
// 普通函数
function createURL(protocol, domain, path) {
  return `${protocol}://${domain}/${path}`;
}

// 柯里化后
const curriedURL = curry(createURL);
const httpsURL = curriedURL('https'); // 固定协议
const githubAPI = httpsURL('api.github.com'); // 固定域名
console.log(githubAPI('users/octocat')); // https://api.github.com/users/octocat

2. 延迟计算

javascript
javascript
下载
复制
// 日志工具
const curriedLog = curry((level, timestamp, message) => {
  console[level](`[${timestamp}] ${message}`);
});

const logWithTime = curriedLog('log')(new Date().toISOString());
logWithTime('用户登录成功'); // [2024-01-15T10:30:00Z] 用户登录成功
logWithTime('数据保存成功'); // 复用时间戳

3. 函数组合

javascript
javascript
下载
复制
// 工具函数
const map = curry((fn, array) => array.map(fn));
const filter = curry((fn, array) => array.filter(fn));
const reduce = curry((fn, initial, array) => array.reduce(fn, initial));

// 组合使用
const numbers = [1, 2, 3, 4, 5];
const add = curry((a, b) => a + b);

// 计算大于2的数的总和
const result = pipe(
  filter(n => n > 2),
  map(n => n * 2),
  reduce(add, 0)
)(numbers);

console.log(result); // 24

4. 配置预设

javascript
javascript
下载
复制
// API请求配置
const apiRequest = curry((method, endpoint, data, headers) => {
  return fetch(endpoint, { method, body: JSON.stringify(data), headers });
});

// 预设配置
const post = apiRequest('POST');
const postJSON = post(null, { 'Content-Type': 'application/json' });

// 使用预设
postJSON('/api/users', { name: '张三' })
  .then(res => res.json());

总结

柯里化的核心价值:

  1. 参数复用:固定某些参数,创建更具体的函数
  2. 延迟执行:参数不完整时不执行,返回新函数
  3. 函数组合:便于创建管道式函数调用
  4. 提高可读性:将多参数函数转换为可链式调用的单参数函数

适用场景:

  • 需要固定部分参数的函数调用
  • 函数式编程中的函数组合
  • 创建可配置的工具函数
  • 提高代码的复用性和可读性

注意事项:

  • 不适合需要动态this的函数
  • 注意默认参数对fn.length的影响
  • 在性能敏感场景考虑缓存优化

参考链接:juejin.cn/post/734569…