【前端】JavaScript中的柯里化(Currying)详解及实现

news2024/11/28 18:50:08

在这里插入图片描述

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]
本文专栏: 前端

文章目录

  • 💯前言
  • 💯什么是柯里化?
  • 💯柯里化的特点
  • 💯柯里化的简单示例
  • 💯通用的柯里化实现
  • 💯柯里化让代码更易读的原因
  • 💯柯里化是否总是更易读?
  • 💯柯里化的实现思路
  • 💯减少 `return` 的场景
  • 💯柯里化的实际用途
  • 💯柯里化的缺点
  • 💯总结


在这里插入图片描述


💯前言

  • JavaScript 编程中,函数是极为灵活强大的工具。近年来,函数式编程风格的流行逐渐改变了开发者对代码结构设计模式的理解。在函数式编程的诸多技术中,柯里化(Currying)占据了一个十分重要的位置。
    对于那些希望编写简洁优雅可重用代码的开发者来说,柯里化无疑是一个值得深入研究的概念。本文旨在全面阐述柯里化的理论基础实现方式实际应用场景及其潜在的优势与局限性,以期为读者提供系统化的认知
    JavaScript在这里插入图片描述

💯什么是柯里化?

在这里插入图片描述

柯里化是一种将接收多个参数的函数转换为一系列只接收单一参数的函数的技术。这种技术在函数式编程中极为常见,其核心思想是将多参数函数拆分为多个一元函数,使得每次调用时仅需处理一个参数,从而逐步构建出完整的结果。通过这样逐层的拆解与组合,柯里化有效地降低了函数的复杂性,并且提高了函数复用的灵活性。

简单来说,假如我们有一个多参数函数 f(a, b, c),经过柯里化之后,它会被转换为 f(a)(b)(c) 的形式。这意味着,每次函数调用仅接受一个参数并返回一个新的函数,直到最终所有的参数都被提供并得到结果。这种逐次处理参数的过程不仅是代码层面的优化,更是一种思维方式上的进步。

柯里化不仅是一种函数转换的技术,它更是一种对函数调用逻辑的重新架构和思考方式。通过逐步处理每个参数,柯里化实现了函数的高度灵活性和精细控制。在实际开发中,柯里化常常与高阶函数结合使用,以实现复杂逻辑的灵活组合,从而构建出更为优雅和可维护的代码架构。其应用场景涵盖了从简单的数学运算到复杂的事件处理,广泛的实践证明了柯里化在函数式编程中的巨大潜力


💯柯里化的特点

在这里插入图片描述

  1. 逐步传递参数:柯里化的核心在于函数每次只接受一个参数,而不是一次性接受全部参数。这使得函数调用可以逐步进行,有助于简化逻辑和减少出错概率
  2. 延迟计算:柯里化允许函数在传入部分参数后不立即执行,而是返回一个新的函数,以便稍后继续接收剩余的参数。这种延迟计算机制对提高程序灵活性和响应性极为有利。
  3. 提高代码复用性:柯里化通过逐步传递参数,可以方便地创建一系列配置相似但略有不同的函数,从而极大地提高了代码复用性
  4. 提升代码的可测试性和可维护性:柯里化后的函数往往较小,且专注于处理单一任务。这种单一功能性的函数更容易进行单元测试,也使得代码维护更加简便。

💯柯里化的简单示例

在这里插入图片描述
为了更好地理解柯里化,我们来看一个具体的例子。假设我们有一个简单的加法函数:

function add(a, b) {
  return a + b;
}

这个函数接受两个参数 ab,直接返回两者的和。如果我们将它柯里化,可以这样做:

function curriedAdd(a) {
  return function(b) {
    return a + b;
  };
}

// 调用方式
const addFive = curriedAdd(5); // 返回一个新函数
console.log(addFive(3)); // 输出 8

在这个例子中,curriedAdd 是一个柯里化后的函数,它每次接收一个参数,返回一个新的函数,直到所有参数都被处理完成。通过柯里化,我们可以创建出例如 addFive 这样有特定用途的新函数,使代码变得更加简洁和可复用。

柯里化的这种方式在某些场景下非常有用,例如当你需要生成一系列类似但略有不同的函数时,柯里化可以方便地固定某些参数值,而不必重复编写函数逻辑。这使得代码显得更加优雅,避免了逻辑上的冗余,同时使得代码变得更加模块化。


💯通用的柯里化实现

在这里插入图片描述
如果你需要对任意的多参数函数进行柯里化,可以编写一个通用的柯里化函数。下面是一个实现的例子:

function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      // 如果传入的参数足够,直接调用原始函数
      return func.apply(this, args);
    } else {
      // 否则返回一个函数,等待更多参数
      return function(...nextArgs) {
        return curried(...args, ...nextArgs);
      };
    }
  };
}

// 示例
function multiply(a, b, c) {
  return a * b * c;
}

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

在这个通用的柯里化函数中,curry(func) 返回一个新的函数 curried,它会检查当前传递的参数数量是否足够调用原函数 func。如果参数足够,就调用原函数并返回结果;否则,返回一个新的函数用于接收剩余的参数。

这种通用柯里化实现不仅可以用于简单的加法或者乘法函数,还可以应用于更加复杂的场景,例如事件处理、API 请求等。在这些场景中,柯里化能够有效地分离参数逻辑,使代码结构更具层次性和可读性。


💯柯里化让代码更易读的原因

在这里插入图片描述

  1. 逐步传参,降低复杂度

传统的函数设计往往要求一次性传入所有参数,这对某些场景来说,直接写出所有参数并不是那么直观或易读。例如:

calculate(10, 20, 30);

在这种情况下,开发者需要明确这些参数分别代表什么,这容易导致代码可读性降低。而柯里化后的函数可以逐步传入参数,每一步都非常明确:

calculate(10)(20)(30);

这种方式每次只关注一个参数,使得代码更加清晰,符合逐步解决问题的逻辑,也让每个函数在功能上保持了单一性,从而降低了整体的复杂度。这种形式尤其适用于处理一系列逐步应用的操作,例如数学计算、字符串处理、或分步骤处理的业务逻辑。

  1. 逻辑分离,提升复用性

柯里化还使得代码的复用性大大增强。通过柯里化后的函数,我们可以生成特定用途的小工具函数。例如:

const add = (a) => (b) => a + b;

const addFive = add(5); // 固定 a 为 5
console.log(addFive(10)); // 输出 15

通过柯里化,我们可以轻松地创建具有特定用途的工具函数,这种简化有助于减少代码的重复,提升开发效率,特别是在需要多次调用相似逻辑的场景下。柯里化后的函数往往具备单一职责,便于模块化和单独测试,这种模块化设计使得开发者能够更加专注于每个独立功能模块的实现。

  1. 更贴近业务逻辑

柯里化可以使代码的调用方式与业务需求更紧密结合。例如,我们可以定义一个用于记录日志的函数:

const log = (level) => (time) => (message) =>
  console.log(`[${level}] [${time}] ${message}`);

const errorLog = log("ERROR");
const nowErrorLog = errorLog(new Date().toISOString());

nowErrorLog("Something went wrong!");

在这个例子中,我们逐步设置日志的级别时间戳,最终传入具体的日志内容。每一步的调用逻辑都非常清晰,这种设计符合人类的思维方式,使得代码的可读性大大增强。

柯里化使得函数调用更加接近自然语言的描述,特别是在需要复杂参数配置的业务逻辑中,可以清晰地表达每个步骤的含义。这样的分步配置方式对于某些高度定制化的功能,诸如日志管理、API 请求设置等,显得尤为高效和灵活。


💯柯里化是否总是更易读?

在这里插入图片描述

  1. 对于简单场景:增加嵌套可能显得多余

对于一些简单的函数,柯里化可能会使代码变得冗长而无益。例如,简单的加法函数直接写成:

function add(a, b) {
  return a + b;
}

在这种情况下,柯里化的多层嵌套可能显得过于繁琐,不如直接使用普通函数来得更为简洁。在这种情况下,增加额外的嵌套不仅不会带来实质性的好处,还可能使代码更加难以理解。

  1. 对于复杂场景:柯里化让逻辑更直观

当函数的参数较多,或者需要灵活地部分应用参数时,柯里化可以帮助逐步清晰地拆分逻辑,显著提升代码的可读性和维护性。对于复杂的业务场景,参数的逐步应用能够显著增强代码的可维护性和逻辑清晰度,从而便于团队协作与理解。


💯柯里化的实现思路

在这里插入图片描述
假如一个函数有五个参数,柯里化之后,这个函数会被逐层嵌套为四个返回函数,每一层函数依次接收一个参数。例如:

function curriedAdd(a) {
  return function(b) {
    return function(c) {
      return function(d) {
        return function(e) {
          return a + b + c + d + e;
        };
      };
    };
  };
}

console.log(curriedAdd(1)(2)(3)(4)(5)); // 输出 15

在这里,每个函数层级都会接收一个参数,直到所有参数都传入完成。这种设计符合柯里化的基本定义,使得每次调用函数时的逻辑都清晰明确。

对于复杂的业务逻辑,通过柯里化,我们可以逐步将一个多参数的问题分解为更小、更简单的部分,使得每一层函数的职责都变得单一且明确,这样的代码更符合“单一职责原则”,有利于代码的复用和单独测试。


💯减少 return 的场景

在这里插入图片描述

如果多层嵌套显得过于复杂,可以通过改进的柯里化实现,允许一次性传入多个参数,从而减少显式嵌套:

function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      return func.apply(this, args);
    } else {
      return function(...nextArgs) {
        return curried(...args, ...nextArgs);
      };
    }
  };
}

// 示例
const add = (a, b, c, d, e) => a + b + c + d + e;

const curriedAdd = curry(add);

console.log(curriedAdd(1, 2)(3)(4, 5)); // 输出 15
console.log(curriedAdd(1)(2)(3, 4, 5)); // 输出 15

这种改进的柯里化方法结合了传统函数和柯里化的优点,让开发者可以灵活选择是逐步传参还是一次性传入多个参数。这样的灵活性在实际开发中尤为重要,因为它让函数的使用更加便捷,适应不同的使用场景。


💯柯里化的实际用途

在这里插入图片描述

  1. 函数复用

    • 柯里化允许你生成固定部分参数的新函数,从而极大地提升了函数的复用性。例如:
    const log = (level, message) => console.log(`[${level}] ${message}`);
    const info = log.bind(null, "INFO");
    const error = log.bind(null, "ERROR");
    
    info("This is an informational message.");
    error("This is an error message。");
    

    通过预绑定部分参数,我们可以轻松创建一些特定用途的函数,例如 infoerror,这些函数共享原始的 log 逻辑,但具有不同的参数配置。

  2. 部分应用

    • 通过柯里化,你可以生成特定用途的函数。例如:
    const fetchWithBaseURL = curry(fetch)("https://api.example.com");
    
    fetchWithBaseURL("/users")
      .then(response => response.json())
      .then(data => console.log(data));
    

    通过柯里化,我们可以创建一个基于特定基 URL 的 fetch 请求函数,使得后续的 API 调用更加简单和清晰,避免了重复代码。

  3. 事件处理和回调

    • 柯里化函数可以提前绑定部分参数,从而减少代码冗余,使代码更加模块化和灵活。例如在事件处理场景中,可以用柯里化将事件和处理逻辑分离:
    const addEventListenerCurried = (element) => (event) => (handler) => {
      element.addEventListener(event, handler);
    };
    
    const button = document.querySelector("#myButton");
    const onClick = addEventListenerCurried(button)("click");
    
    onClick(() => alert("Button clicked!"));
    

    通过这样的方式,addEventListenerCurried 将元素、事件类型和处理逻辑分开,使得每一步都非常清晰且容易复用。


💯柯里化的缺点

在这里插入图片描述

  1. 增加嵌套,容易混淆:对于一些初学者来说,多层嵌套的函数写法可能不太容易理解,尤其是嵌套层数较多时
  2. 不适用于所有场景:对于简单的函数或不需要逐步传参的场景,柯里化显得不必要且冗长
  3. 调试复杂度增加:由于柯里化函数返回的是一层层嵌套的函数,调试时可能难以直观地查看所有的调用与参数,这增加了调试的难度
  4. 性能开销:在性能敏感的场景下,柯里化可能带来额外的函数调用开销,尤其是在深度嵌套的情况下,可能会影响代码的执行效率。

💯总结

  • 在这里插入图片描述
    柯里化是一种极具威力的函数式编程技术,通过将多参数函数逐步分解为单参数函数,能够有效提升代码的复用性和可读性。在 JavaScript 中,柯里化能够帮助开发者更为模块化和清晰地组织代码,特别是当需要部分应用或者分步处理逻辑时,其优势尤为显著。
    尽管柯里化并非适用于所有情况,在某些简单场景下可能显得复杂化,但掌握柯里化的思想能够极大丰富开发者解决问题的方式。当使用柯里化时,关键在于权衡其带来的灵活性与复杂度,根据具体情境选择最适合的代码组织方式。希望本文的详细阐述能够帮助你更好地理解和运用柯里化,提升代码的质量和可维护性。
    柯里化不仅是技术上的转变,更是一种思维方式的革新。它使得函数更加灵活且高度可复用,并且让代码逻辑更接近自然语言描述。通过合理应用柯里化,你可以编写更加优雅、扩展性更强且可维护性更高的代码。无论是在前端还是后端开发中,柯里化都为你提供了一种全新的思维范式,帮助你编写出更加简洁、优雅且强大的代码解决方案。

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2249249.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot 整合 rabbitMQ (延迟队列)

前言: 延迟队列是一个内部有序的数据结构,其主要功能体现在其延时特性上。这种队列存储的元素都设定了特定的处理时间,意味着它们需要在规定的时间点或者延迟之后才能被取出并进行相应的处理。简而言之,延时队列被设计用于存放那…

Java代码操作Zookeeper(使用 Apache Curator 库)

1. Zookeeper原生客户端库存在的缺点 复杂性高:原生客户端库提供了底层的 API,需要开发者手动处理很多细节,如连接管理、会话管理、异常处理等。这增加了开发的复杂性,容易出错。连接管理繁琐:使用原生客户端库时&…

Django实现智能问答助手-基础配置

设置 Django 项目、创建应用、定义模型和视图、实现问答逻辑,并设计用户界面。下面是一步一步的简要说明: 目录: QnAAssistant/ # 项目目录 │ ├── QnAAssistant/ # 项目文件夹 │ ├── init.py # 空文件 │ ├── settings.py # 项目配…

【ESP32CAM+Android+C#上位机】ESP32-CAM在STA或AP模式下基于UDP与手机APP或C#上位机进行视频流/图像传输

前言: 本项目实现ESP32-CAM在STA或AP模式下基于UDP与手机APP或C#上位机进行视频流/图像传输。本项目包含有ESP32源码(arduino)、Android手机APP源码以及C#上位机源码,本文对其工程项目的配置使用进行讲解。实战开发,亲测无误。 AP模式,就是ESP32发出一个WIFI/热点提供给电…

从〇开始深度学习(0)——背景知识与环境配置

从〇开始深度学习(0)——背景知识与环境配置 文章目录 从〇开始深度学习(0)——背景知识与环境配置写在前面1.背景知识1.1.Pytorch1.2.Anaconda1.3.Pycharm1.4.CPU与GPU1.5.整体关系 2.环境配置2.1.准备工作2.1.1.判断有无英伟达显卡2.1.2.清理电脑里的旧环境 2.1.安装Anaconda…

mac下Gpt Chrome升级成GptBrowser书签和保存的密码恢复

cd /Users/自己的用户名/Library/Application\ Support/ 目录下有 GPT\ Chrome/ Google/ GptBrowser/ GPT\ Chrome 为原来的chrome浏览器的文件存储目录. GptBrowser 为升级后chrome浏览器存储目录 书签所在的文件 Bookmarks 登录账号Login 相关的文件 拷贝到GptBrow…

圆域函数的傅里叶变换和傅里叶逆变换

空域圆域函数的傅里叶变换 空域圆域函数(也称为空间中的圆形区域函数)通常指的是在二维空间中,以原点为中心、半径为 a a a的圆内取值为1,圆外取值为0的函数。这种函数可以表示为: f ( x , y ) { 1 if x 2 y 2 ≤ …

Java基础——类型转化(强制转化)

目录 1.数字间的类型转换 (1) 隐式类型转换 (2)显式类型转换(强制类型转换) 2.类对象间的强制转换 (1) 向上转型 (2) 向下转型 将一个类型强制转换成另…

数据结构C语言描述5(图文结合)--广义表讲解与实现

前言 这个专栏将会用纯C实现常用的数据结构和简单的算法;有C基础即可跟着学习,代码均可运行;准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言…

23种设计模式-装饰器(Decorator)设计模式

文章目录 一.什么是装饰器设计模式?二.装饰器模式的特点三.装饰器模式的结构四.装饰器模式的优缺点五.装饰器模式的 C 实现六.装饰器模式的 Java 实现七.代码解析八.总结 类图: 装饰器设计模式类图 一.什么是装饰器设计模式? 装饰器模式&…

构建英语知识网站:Spring Boot框架解析

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…

数据结构之数组与链表的差异

一、数组 数组(Array)是由相同类型的元素(element)的集合所组成的数据结构,分配一块连续的内存来存储。利用元素的索引(index)可以计算出该元素对应的存储地址。最简单的数据结构类型是一维数组…

RabbitMQ7:消息转换器

欢迎来到“雪碧聊技术”CSDN博客! 在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将…

Ubuntu20.04+ROS 进行机械臂抓取仿真:环境搭建(一)

目录 一、从官网上下载UR机械臂 二、给UR机械臂添加夹爪 三、报错解决 本文详细介绍如何在Ubuntu20.04ROS环境中为Universal Robots的UR机械臂添加夹爪。首先从官方和第三方源下载必要的软件包,包括UR机械臂驱动、夹爪插件和相关依赖。然后,针对gazeb…

(即插即用模块-Attention部分) 二十、(2021) GAA 门控轴向注意力

文章目录 1、Gated Axial-Attention2、代码实现 paper:Medical Transformer: Gated Axial-Attention for Medical Image Segmentation Code:https://github.com/jeya-maria-jose/Medical-Transformer 1、Gated Axial-Attention 论文首先分析了 ViTs 在训…

Git 进程占用报错-解决方案

背景 大仓库,由于开发者分支较多,我们在使用 git pull 或 git push 等命令时(与远端仓库交互的命令),不知之前配置了什么,我的电脑会必现以下报错(有非常长一大串报错-不同分支的git进程占用报…

【FPGA-MicroBlaze】串口收发以及相关函数讲解

前言 工具:Vivado2018.3及其所对应的SDK版本 目前网上有许多MicroBlaze 的入门教程,比如下面的这个参考文章,用串口打印一个hello world。 【FPGA】Xilinx MicroBlaze软核使用第一节:Hello World!_fpga软核microblaze-CSDN博客 个…

代码美学2:MATLAB制作渐变色

效果: %代码美学:MATLAB制作渐变色 % 创建一个10x10的矩阵来表示热力图的数据 data reshape(1:100, [10, 10]);% 创建热力图 figure; imagesc(data);% 设置颜色映射为“cool” colormap(cool);% 在热力图上添加边框 axis on; grid on;% 设置热力图的颜色…

Android下载出现open failed: EPERM (Operation not permitted)

今天帮忙给同事调一下apk,发现android 自动更新apk,下载apk的时候总是失败,总是卡在 输出流这一步了 于是第一步分析,立马想到权限 但是下载之前的读写内存的权限也都有了 什么android 10高版本的不开启分区存储也用了 android…

使用爬虫时,如何确保数据的准确性?

在数字化时代,数据的准确性对于决策和分析至关重要。本文将探讨如何在使用Python爬虫时确保数据的准确性,并提供代码示例。 1. 数据清洗 数据清洗是确保数据准确性的首要步骤。在爬取数据后,需要对数据进行清洗,去除重复、无效和…