用JavaScript的管道方法简化代码复杂性

用JavaScript的管道方法简化代码复杂性

在现代 web 开发中,维护干净有效的代码是必不可少的。随着项目的增加,我们功能的复杂性也在增加。然而,javaScript为我们提供了一个强大的工具,可以将这些复杂的函数分解为更小的、可管理的片段。在本文中,我们将探索使用 pipe 方法并通过一个真实的场景展示它的好处。

这是一个基本的pipe:
在这里插入图片描述

让我们从需要解决的问题开始

我们要计算用户购买各种产品时的最终价格。首先,有些产品有折扣的条件,所以我们想使用折扣对象,以便对产品的原价进行折扣计算。然后,我们要计算折扣价格的总和。如果顾客有优惠券,我们也需要考虑到这一点。最后,在以一种货币计算价格之后,将其转换成另一种货币,我们将最终价格交付给客户。

在这里插入图片描述
数据:

  • userPurchases:代表用户购买的产品清单。每件物品都有一个唯一的标识符(id)、产品名称(name)、价格 (price)、及价格的货币(currency)。
  • DISCOUNT_MAP:它将产品标识与折扣百分比联系起来。
  • COUPON_USD:客户持有的美元折扣券价值,可从总购货价格中减去。
  • USD_TO_EUR:美元兑换成欧元的兑换率保持不变。
const userPurchases = [
  {
    id: 101,
    price: 34.99,
    name: 'Wireless Headphones',
    currency: 'USD',
  },
  {
    id: 202,
    price: 149.95,
    name: 'Digital Camera Kit',
    currency: 'USD',
  },
  {
    id: 303,
    price: 19.99,
    name: 'Home Gym Equipment',
    currency: 'USD',
  },
];

const DISCOUNT_MAP = new Map([
  [101, 10],
  [303, 20],
  [404, 30],
]);

const COUPON_USD = 75;
const USD_TO_EUR = 0.94;

在我们的设想中,将执行以下步骤:

  • 根据折扣对象计算新价格。
  • 计算产品的总和。
  • 减去优惠券的。
  • 将结果从美元改为欧元。

以下是初步实施情况:

const calculateFinalPrice = (
  userPurchases,
  discountMap,
  userCoupon,
  conversionRate,
) => {
  //定义一个计算项目折扣价格的函数。
  const calculateDiscount = (price, discount = 0) => {
    if (discount < 0 || discount > 100) return price;
    if (price < 0) return NaN;
  
    return price * ((100 - discount) / 100);
  };

  //对使用折扣的物品实行折扣。
  const itemsWithDiscount = userPurchases.map(item => ({
    ...item,
    price: calculateDiscount(item.price, discountMap.get(item.id)),
  }));

 //计算所有项目的价格总额。
  const total = itemsWithDiscount.reduce((acc, item) => acc + item.price, 0);

  //从总数中减去用户的优惠券价值。
  const totalAfterCoupon = total - userCoupon;

  //转换成一种特定的货币。
  const finalPrice = totalAfterCoupon * conversionRate;

  return finalPrice;
};

如果我们仔细看代码,就会发现我们通过以下步骤解决了这个问题:

在这里插入图片描述

这种方法有什么不好的?

我们只在一个函数中就解决了所有这些步骤。如果我们在这4个步骤中出现了一个 bug 呢?我们就必须找出我们的函数哪一部分导致了这个问题,这个过程中我们就可能需要花费大量时间去排查问题。

所以,下面让我们看看如何实现 pipe 方法来解决这个问题。

pipe 方法

pipe 方法允许我们将一个大的、复杂的函数分解为更小的、可组合的函数。

这是一个简单实现这个函数的例子:

const pipe = (...fns) => (arg) => fns.reduce((v, fn) => fn(v), arg);

这个想法很简单,将多个函数组合起来,从左到右依次应用它们,使用前一个函数的输出作为下一个函数的输入。

例如,如果我们有函数A、B和C,并且希望它们按照 A ->B -> C 流程应用于某些数据x,我们可以使用像这样使用 pipe:

const result = pipe(
  funcA,
  funcB,
  funcC
)(x);

以下是它的工作原理:

  • pipe 以一个或多个功能作为输入,…fns 部分意味着我们可以提供任意数量的函数,用逗号分隔,它们将被视为一个列表。
  • 它返回一个新的函数,可以接受某些输入数据,用 arg 表示。这个输入数据可以是任何东西,如数字、文本或更复杂的信息。
  • 在这个新函数中,它使用了reduce 将列表中的每个函数应用到输入数据中,一个接一个。把它看作是通过一系列的处理步骤传递输入。
  • reduce 函数负责逐步处理。从 arg 这个原始输入开始,并将列表中的第一个函数应用到列表中。然后,它获取结果并应用下一个函数,以此类推,直到它完成列表中的所有函数。
  • 最后,它返回将所有这些函数应用于输入数据的最终结果。

现在,让我们应用这些概念:

//定义一个计算项目折扣价格的函数。
const calculateDiscount = (price, discount = 0) => {
  if (discount < 0 || discount > 100) return price;
  if (price < 0) return NaN;

  return price * ((100 - discount) / 100);
}

// 创建一个函数使用discountMap
const applyDiscounts = discountMap => items =>
  items.map((item) => ({
    ...item,
    price: calculateDiscount(item.price, discountMap.get(item.id)),
  }));

// 定义一个计算所有项目价格合计的函数。
const calculateTotal = items => 
  items.reduce((total, item) => total + item.price, 0);

//创建一个函数,从中减去用户的优惠券价值。
const subtractCoupon = coupon => total => total - coupon;

//转换成一种特定的货币。
const convertTo = conversionRate => total => total * conversionRate;

const calculateFinalPrice = (items, discountMap, coupon, conversionRate) =>
  pipe(
    applyDiscounts(discountMap),
    calculateTotal,
    subtractCoupon(coupon),
    convertTo(conversionRate),
  )(items);

我们从用户购买的数据开始,在每个步骤中,我们应用一个特定的函数并转换数据,直到我们得到最终结果。

就像这样:

在这里插入图片描述

这种办法的好处是什么?

我们已经创建了几个较小的、专门的函数来解决一个具体的问题。我们现在可以在应用程序的其他地方使用它们。我们可以分别测试每个函数。

例如,如果我们想测试折扣计算功能,我们可以这样做:

import { calculateDiscount } from './calculateDiscount';

describe('calculateDiscount function', () => {
  it('当折扣为负数时,应退回原价。', () => {
    const price = 100;
    const discount = -10;
    expect(calculateDiscount(price, discount)).toBe(100);
  });

  it('当折扣为零时,应退回原价。', () => {
    const price = 100;
    const discount = 0;
    expect(calculateDiscount(price, discount)).toBe(100);
  });
});

结论

通过将复杂的流程分解为较小的可测试单元,我们可以提高代码质量,减少错误,并使我们的代码库更容易理解。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SSM志愿者系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 志愿者服务网站系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库 &#xff0c;系统主要采用B…

Jmeter组件执行顺序与作用域

一、Jmeter重要组件 1&#xff09;配置元件---Config Element&#xff1a; 用于初始化默认值和变量&#xff0c;以便后续采样器使用。配置元件大其作用域的初始阶段处理&#xff0c;配置元件仅对其所在的测试树分支有效&#xff0c;如&#xff0c;在同一个作用域的任何采样器…

shareMouse 使用中遇到的问题

一、shareMouse 使用中遇到的问题 1、鼠标不能移动到另一个显示器 明明是两个显示器&#xff0c;但是 只显示一个&#xff0c;鼠标也不能移到另一个显示器上 后来&#xff0c; 设置了 wrap mouse pointer around display就好了&#xff0c;虽然还是显示一个显示器&#xff0c…

基于SpringBoot的图书推荐系统的

摘 要 网络信息技术的高速发展&#xff0c;使得高校图书馆的服务空间日益扩大&#xff0c;依据个人特点的针对性服务逐渐成为新服务模式的主导趋势。对于大多数用户而言&#xff0c;很难在大量的学术图书馆中快速找到他们想要的材料。另外&#xff0c;随着时代的不断发展&…

RocketMQ阅读源码前的准备

本文将讲解如何在IDEA中导入 RocketMQ 源码&#xff0c;并运行 Broker 和 NameServer&#xff0c;编写一个消息发送与消息消费的示例。 一. 源码导入及调试 1.1 导入源码 RocketMQ 原先是阿里巴巴集团内部的消息中间件&#xff0c;于2016年提交至Apache基金会孵化&#xff0…

高压功率放大器的应用领域有哪些

高压功率放大器是一种特殊的电子设备&#xff0c;用于放大低电压信号到较高的功率水平。它在许多应用领域中发挥着重要作用。下面西安安泰将详细介绍高压功率放大器的几个常见应用领域。 声学领域&#xff1a;高压功率放大器在声学领域中广泛应用。例如&#xff0c;在音响系统和…

第一节:认识微服务

一、微服务技术对比 Dubbo SpringCloudSpringCloudAlibaba注册中心zookeeper、Redis Eureka、ConsulNacos、Eureka服务远程调用Dubbo协议Feign&#xff08;http协议&#xff09;Dubbo、Feign配置中心无SpringCloudGateway、ZuulSpringCloudConfig、Nacos服务网…

VR 实现 Splash Screen 效果

文章目录 背景官方实现逆向分析 背景 手机 App 在实现 Splash Screen 的时候&#xff0c;目前都有成熟的方案可以参考&#xff0c;但是在做 VR 开发时&#xff0c;要如何实现一个 App 自己的 Splash Screen &#xff0c;下面是我们基于 PICO & OCULUS 进行业务开发时经过探…

继承 和 多肽(超重点 ! ! !)

[本节目标] 1.继承 2.组合 3.多肽 1.继承 1.1 为什么要继承 Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0…

Prometheus+Grafana搭建日志采集

介绍 一、什么是日志数据采集 日志数据采集是指通过各种手段获取应用程序运行时产生的各类日志信息&#xff0c;并将这些信息存储到特定的地方&#xff0c;以便后续分析和使用。通常情况下&#xff0c;这些日志信息包括系统运行状态、错误信息、用户操作记录等等。通过对这些…

网络基础:网络通信基础

目录 1.网络通信基本单位 2.网络通信基础 3.调制技术 4.解调技术 5.载波调制 6.编码技术 6.1基本编码 6.2应用型编码 1.曼彻斯特编码 2.差分曼彻斯特编码 3.MLT-3编码 4.mB/nB编码 1.网络通信基本单位 Byte&#xff08;字节&#xff09;是用于计量存储容量的一种…

【动手学深度学习】(八)数值稳定和模型初始化

文章目录 一、理论知识 一、理论知识 1.神经网络的梯度 考虑如下有d层的神经网络 计算损失l关于参数Wt的梯度&#xff08;链式法则&#xff09; 2.数值稳定性常见的两个问题 3.梯度爆炸 4.梯度爆炸的问题 值超出阈值 对于16位浮点数尤为严重 对学习率敏感 如果学习率太大…

游戏反Frida注入检测方案

在游戏安全对抗过程中&#xff0c;有不少外挂的实现基于对游戏内存模块进行修改&#xff0c;这类外挂通常会使用内存修改器&#xff0c;除此之外&#xff0c;还有一种门槛相对更高、也更难检测的「注入挂」。 据FairGuard游戏安全数据统计&#xff0c;在游戏面临的众多安全风险…

oops-framework框架 之 创建项目(二)

引擎&#xff1a; CocosCreator 3.8.0 环境&#xff1a; Mac Gitee: oops-game-kit 构建 本篇博客将使用oops-game-kit 构建一个新的开发项目&#xff0c; 关于 oops-framework 框架的了解&#xff0c;可参考上篇博客&#xff1a; oops-framework框架 之 初始了解(一) 大概…

11.2每日一题(函数定义域)

因为等式的左边是单调增的&#xff0c;所以右边的等式也需要单调增从而确定函数的定义域

【C语言】【字符串函数的模拟实现】strcpy,strcat,strcmp,strncpy,strncat,strstr

1.strcpy char* strcpy(char*destination,const char* source)源字符串必须以’\0’结尾会将原字符串中的‘\0’拷贝到目标字符串中目标空间必须足够大&#xff0c;能放得下源字符串 模拟实现&#xff1a; char *my_strcpy(char* des,const char *sour) {char* retdes;asser…

接口测试探索基础

接口基础知识理解&#xff1a; 接口一般来说有两种&#xff1a;程序接口和协议接口 程序接口&#xff1a;程序内部的接口、倾向于方法间的调通信方式 协议接口&#xff1a;系统对外的接口、其他应用通过授权或认证后获取数据的方式 常见的的接口&#xff1a; 1、webService接口…

海云安谢朝海:开发安全领域大模型新实践 人工智能助力高效安全左移

2023年11月29日&#xff0c;2023中国&#xff08;深圳&#xff09;金融科技大会成功举行&#xff0c;该会议是深圳连续举办的第七届金融科技主题年度会议&#xff0c;也是2023深圳国际金融科技节重要活动之一。做好金融工作&#xff0c;需要兼顾创新与安全&#xff0c;当智能体…

Adobe:真正持续突破需要更多增长

如果我们调出 Adobe&#xff08;NASDAQ&#xff1a;ADBE &#xff09;的长期图表&#xff0c;我们会发现自 2011 年以来&#xff0c;长期投资者尤其获得了非常丰厚的回报。此外&#xff0c;该股票在去年 9 月为投资者提供了绝佳的机会由于股票在过去 14 个月里的回报率远超 100…

SQL Server 数据库,使用函数查询统计信息

4.1 在查询中使用函数 在前面章节已经学习了一些简单的增、删、改、查询的T-SOL.语句&#xff0c;但是为了更方便快捷地完 成大量的任务&#xff0c;SOLServer提供了一些内部函数&#xff0c;可以和SOLServer的SELECT语句联合使用&#xff0c;也可 以与UPDATE和INSERT一起使用&…