Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用

详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用

作者头像
watermelo37
发布于 2025-01-22 13:27:36
发布于 2025-01-22 13:27:36
12100
代码可运行
举报
文章被收录于专栏:前端专精前端专精
运行总次数:0
代码可运行

作者:watermelo37 涉及领域:Vue、SpingBoot、Docker、LLM、python等 --------------------------------------------------------------------- 温柔地对待温柔的人,包容的三观就是最大的温柔。 ---------------------------------------------------------------------

详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用

柯里化(Currying)是函数式编程中的一颗璀璨明珠,它不仅提升了代码的灵活性与可重用性,还提供了一种全新的函数设计思路。本篇博客将带你深入了解柯里化,从概念到实际应用,从简单实现到底层机制,全面解析这一强大的编程技术。

一、什么是柯里化?

1、原理解析

柯里化是将一个接受多个参数的函数转换为一系列单参数函数的过程。换句话说,柯里化后的函数会逐步接收参数,每次只接收一个或部分参数,直到收集到足够的参数时才真正执行。

柯里化的概念源于逻辑学家哈斯凯尔·柯里(Haskell Curry),他的研究为函数式编程奠定了理论基础。如今,柯里化已成为现代编程语言中的重要组成部分,尤其在函数式编程语言(如 Haskell、Scala)和 JavaScript 等动态语言中广泛应用。

2、一个直观的例子

假设我们有一个普通的求和函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function sum(a, b, c) {
  return a + b + c;
}

使用柯里化后,我们可以这样调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const curriedSum = curry(sum);
const result = curriedSum(1)(2)(3); // 6

甚至可以分步调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const partialSum = curriedSum(1); // 固定第一个参数
const result = partialSum(2)(3); // 6

这种灵活的调用方式得益于柯里化的设计。

curry(柯里化的核心函数)是一种用于将多参数函数转化为逐步接收参数的函数的工具。在 JavaScript 中,curry 函数通常实现为一个高阶函数,它接收一个原始函数作为参数,并返回一个新的函数,可以逐个或分批接收原始函数的参数。

二、如何实现柯里化?

1、底层实现

让我们从零实现一个通用的 curry 函数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2));
      };
    }
  };
}
2、工作原理解析
  1. 初次调用:外层 curry 函数接受一个目标函数 fn。
  2. 参数累积:curried 函数检测当前参数数量是否满足 fn 的参数要求(fn.length)。
  3. 递归调用:如果未满足,则返回一个新函数,继续接收剩余参数。
  4. 执行原函数:当参数数量足够时,调用 fn 并返回结果。
3、测试我们的实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function multiply(a, b, c) {
  return a * b * c;
}

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


const curriedMultiply = curry(multiply);
console.log(curriedMultiply(2)(3)(4)); // 24
console.log(curriedMultiply(2, 3)(4)); // 24
console.log(curriedMultiply(2)(3, 4)); // 24

三、柯里化的优点

1. 参数复用

通过固定部分参数,柯里化使函数调用更加灵活。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function greet(greeting, name) {
  return `${greeting}, ${name}!`;
}

const sayHello = curry(greet)("Hello");
console.log(sayHello("Alice")); // Hello, Alice!
console.log(sayHello("Bob"));   // Hello, Bob!
2. 延迟执行

柯里化允许延迟计算,直到所有参数准备就绪。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const add = curry((a, b) => a + b);
const addFive = add(5);
console.log(addFive(10)); // 15
3. 函数组合

柯里化与函数组合结合使用时,能够提升代码的可读性和模块化程度。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const compose = (f, g) => (x) => f(g(x));
const addOne = (x) => x + 1;
const double = (x) => x * 2;

const addOneThenDouble = compose(double, addOne);
console.log(addOneThenDouble(3)); // 8

通过柯里化,我们可以轻松定义和组合类似的函数。

四、为什么柯里化如此强大?

柯里化的核心在于其函数设计理念,它将复杂的操作分解为简单的步骤,每个步骤只关注一个变量。这种设计符合数学中 "单一变量函数" 的思想,使代码更加纯粹、可测试和易于推导。

此外,柯里化为函数式编程的核心特性 "无副作用" 提供了有力支持。柯里化函数通常不会修改全局状态,而是通过参数传递上下文,使代码更加健壮。

五、实际应用场景

1. 高阶函数:Redux Selector 的场景模拟

在 Redux 的 reselect 库中,柯里化用于创建高性能的 memoized 选择器:

假设我们管理一个电商网站,用户的购物车和订单状态保存在 Redux 状态树中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const selectCart = (state) => state.cart;
const selectCartItems = createSelector(
  selectCart,
  (cart) => cart.items
);

const selectCartTotal = createSelector(
  selectCartItems,
  (items) => items.reduce((total, item) => total + item.price * item.quantity, 0)
);

const cartTotal = selectCartTotal(state);
console.log(`Cart Total: $${cartTotal}`);

在这个例子中,柯里化使得创建高性能的派生数据函数变得轻而易举,避免了重复计算。

2. 数据管道:构建复杂的数据流

在实际场景中,柯里化可以用于构建复杂的数据流处理。

例如,一个社交媒体平台需要处理一系列数据操作,包括过滤敏感词、格式化文本、统计字数等:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const filterSensitiveWords = curry((words, text) => 
  words.reduce((acc, word) => acc.replace(new RegExp(word, 'gi'), '***'), text)
);
const formatText = (text) => text.trim().toLowerCase();
const countWords = (text) => text.split(/\s+/).length;

const processTextPipeline = compose(
  countWords,
  formatText,
  filterSensitiveWords(['badword', 'offensive'])
);

const result = processTextPipeline(" This is a BadWord example! Badword is offensive. ");
console.log(result); // 输出处理后的字数

这种数据管道式的构建方式,结合柯里化和函数组合,极大提升了代码的可读性和维护性。

3. 深度嵌套函数的优化

柯里化还能简化深度嵌套的调用,避免回调地狱。例如,假设我们要处理一系列 API 数据请求:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const fetchWithAuth = curry((authToken, endpoint) => 
  fetch(endpoint, { headers: { Authorization: `Bearer ${authToken}` } })
);

const fetchUserData = fetchWithAuth('my-secure-token');

Promise.all([
  fetchUserData('/api/user/1'),
  fetchUserData('/api/user/2')
])
  .then(([user1, user2]) => Promise.all([user1.json(), user2.json()]))
  .then(console.log)
  .catch(console.error);

通过柯里化,我们可以轻松创建基于上下文的高阶函数,避免了大量重复代码。

六、总结

柯里化是一种强大的函数式编程技术,它通过将函数分解为单参数形式,实现了灵活性与可复用性的统一。无论是参数复用、延迟执行,还是函数组合,柯里化都为现代编程提供了极大的便利。

从 Redux 的选择器优化到复杂的数据流处理,再到深度嵌套的函数优化,柯里化在实际开发中展现出了非凡的价值。如果你希望编写更简洁、更优雅的代码,柯里化无疑是一个值得深入学习和实践的工具。从简单的实现到复杂的应用,希望这篇博客能为你揭开柯里化的奥秘,助力你的开发之旅!

只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-01-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
聊聊jest的NodeChecker
jest-common-6.3.1-sources.jar!/io/searchbox/client/config/discovery/NodeChecker.java
code4it
2019/04/21
1.1K0
聊聊jest的NodeChecker
聊聊httpclient的evict操作
org/apache/http/impl/client/HttpClientBuilder.java
code4it
2023/10/06
5230
Elasticsearch-Jest 配置ES集群&源码解读
直接访问 https://github.com/searchbox-io/Jest ,把源码拉下来
小小工匠
2021/08/17
8880
高并发场景下的httpClient优化使用
1.背景 我们有个业务,会调用其他部门提供的一个基于http的服务,日调用量在千万级别。使用了httpclient来完成业务。之前因为qps上不去,就看了一下业务代码,并做了一些优化,记录在这里。 先对比前后:优化之前,平均执行时间是250ms;优化之后,平均执行时间是80ms,降低了三分之二的消耗,容器不再动不动就报警线程耗尽了,清爽~ 2.分析 项目的原实现比较粗略,就是每次请求时初始化一个httpclient,生成一个httpPost对象,执行,然后从返回结果取出entity,保存成一个字符串,最后显
老白
2018/03/19
6.9K0
聊聊httpclient的evict操作
org/apache/http/impl/client/HttpClientBuilder.java
code4it
2023/10/06
3070
聊聊httpclient的evict操作
聊聊dubbo的NettyServer
dubbo-2.7.3/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java
code4it
2019/08/30
8770
聊聊dubbo的NettyServer
聊聊spring的async注解
这里讨论一下getAsyncExecutor这里定义null的情况。 spring-context-4.3.9.RELEASE-sources.jar!/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java
code4it
2018/09/17
1.7K0
聊聊Elasticsearch的EsThreadPoolExecutor
本文主要研究一下Elasticsearch的EsThreadPoolExecutor
code4it
2019/06/02
5270
聊聊Elasticsearch的EsThreadPoolExecutor
聊聊springboot jest autoconfigure
spring-boot-autoconfigure-2.1.4.RELEASE-sources.jar!/org/springframework/boot/autoconfigure/elasticsearch/jest/JestProperties.java
code4it
2019/04/20
1.3K0
聊聊springboot jest autoconfigure
聊聊apache gossip的ActiveGossiper
incubator-retired-gossip/gossip-base/src/main/java/org/apache/gossip/manager/AbstractActiveGossiper.java
code4it
2019/05/04
4590
聊聊apache gossip的ActiveGossiper
聊聊hystrix的queueSizeRejectionThreshold参数
本文主要研究一下hystrix的queueSizeRejectionThreshold参数
code4it
2018/09/17
1.5K0
聊聊ExecutorService的监控
metrics-core-4.0.2-sources.jar!/com/codahale/metrics/InstrumentedExecutorService.java
code4it
2018/10/18
1.3K0
聊聊flink的ScheduledExecutor
java.base/java/util/concurrent/Executor.java
code4it
2019/03/13
1.1K0
聊聊flink的ScheduledExecutor
聊聊dubbo的ConnectionOrderedDispatcher
本文主要研究一下dubbo的ConnectionOrderedDispatcher
code4it
2019/09/01
6320
聊聊dubbo的ConnectionOrderedDispatcher
HttpComponents HttpClient连接池(6)-连接清理
在上一篇文章里我们介绍了 httpclient 连接池中连接的可用性检查,在这里我们主要介绍空闲 http 连接的清理。对于连接池中的连接基本都是复用的,其中避免不了 server 端主动关闭连接,这个时候取出的连接自然是不可用的,当然可以通过上一篇文章中的可用性检查避免。但同时 httpclient 连接池也提供了 http 连接的清理策略,用来对连接进行清除。
TA码字
2020/04/01
3.4K0
httpclient参数配置
默认的话,是从response里头读timeout参数的,没有读到则设置为-1,这个代表无穷,这样设置是有点问题了,如果是https链接的话,则可能会经常报
code4it
2018/09/17
7.1K0
聊聊spring cloud的AbstractLoadBalancingClient
本文主要研究一下spring cloud的AbstractLoadBalancingClient
code4it
2018/09/17
1.3K0
聊聊claudb的NotificationManager
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/event/NotificationManager.java
code4it
2020/09/03
1730
聊聊claudb的NotificationManager
聊聊jedis的testWhileIdle
org/apache/commons/pool2/impl/GenericObjectPool.java
code4it
2023/09/23
2910
聊聊spring cloud eureka的instanceEnabledOnit属性
本文主要研究下spring cloud eureka的instanceEnabledOnit属性
code4it
2018/09/17
7410
相关推荐
聊聊jest的NodeChecker
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验