【玩转 JS 函数式编程_010】3.2 JS 函数式编程筑基之:以函数式编程的方式活用函数(上)

news2024/10/10 17:03:23

写在前面
按照惯例,过长的篇幅分开介绍,本篇为 JavaScript 函数式编程核心基础的第二部分——以函数式编程的方式活用函数的上篇,分别介绍了 JS 函数在排序、回调、Promise 期约、以及连续传递等应用场景下的用法演示。和之前章节相比难度又有一定的提升。准备好了吗?

3.2. 以函数式编程的方式使用函数 Using functions in FP ways

有几种常见的编码模式实际上采用了函数式编程的风格,而您甚至都未曾察觉。本节将逐一考察这些模式的具体表现形式,以便您更快地习惯这种编码风格。这些模式包括:

  • 注入模式:用于筛选不同策略及其他用途;
  • 回调与期约模式:将引入连续传值的相关概念;
  • 填充与插入模式;
  • 立即调用策略模式。

3.2.1. 注入——整理出来 Injection – sorting it out

Array.prototype.sort() 方法提供了第一个将函数作为参数传递的示例。 给定一个待排序的字符串数组,则可以调用 array.sort() 方法。例如,将彩虹的颜色按字母顺序排序,代码如下:

var colors = [
  "violet",
  "indigo",
  "blue",
  "green",
  "yellow",
  "orange",
  "red"
];
colors.sort();
console.log(colors);
// ["blue", "green", "indigo", "orange", "red", "violet", "yellow"]

注意,这里的 sort() 方法并不需要任何参数,数组也能完成排序。默认情况下,此方法按字符串的 ASCII 编码进行排序。因此,如果用它对数字型数组排序则会出错,按这种方式得出的结果,数字 20 将位于 1003 之间,因为 10020 之前(排序元素均被视作字符串)而 20 又在 3 之前:

var someNumbers = [3, 20, 100];
someNumbers.sort();

console.log(someNumbers);
// [100, 20, 3]

假如不考虑数字,只对字符串按默认规则排序。此时如果要对一组西班牙语单词(palabras)进行排序,在遵循恰当的本地化语言环境规则时又会如何呢?可以看到,结果并不正确:

var palabras = ["ñandú", "oasis", "mano", "natural", "mítico", "musical"];
palabras.sort();

console.log(palabras);
// ["mano", "musical", "mítico", "natural", "oasis", "ñandú"] -- wrong result!

拓展知识

对于语言或生物学爱好者而言,ñandú 的英文是 rhea,它一种类似于鸵鸟的飞禽。虽然以 ñ 开头的西班牙语单词并不多,而笔者的国家乌拉圭恰好就有这些鸟类——这就是存在特殊单词的客观原因。

哎呀!在西班牙语中,ñ 介于 no 之间,但 ñandú 排到了末尾。此外,mítico(对应英文 mythical,注意重音字母 í)本应出现在 manomusical 之间,波浪号应该被忽略。要解决这个问题,需要向 sort() 传入正确的比较函数。本例可以使用 localeCompare() 方法:

palabras.sort((a, b) => a.localeCompare(b, "es"));

console.log(palabras);
// ["mano", "mítico", "musical", "natural", "ñandú", "oasis"]

这里的语句 a.localeCompare(b,"es") 会对 ab 进行比较:根据西班牙语("es")的排序规则,当 a 先于 b 时返回一个负值;a 落后于 b 时返回一个正值;两者相等时返回 0

现在排序结果正确了!此时可引入一个新函数 spanishComparison() 来替换所需的字符串比较规则,可使代码更加清晰:

const spanishComparison = (a, b) => a.localeCompare(b, "es");

palabras.sort(spanishComparison);
// sorts the palabras array according to Spanish rules:
// ["mano", "mítico", "musical", "natural", "ñandú", "oasis"]

在接下来的章节中,我们将讨论函数式编程如何让您以更贴近声明式的方式来编写代码,生成更易于理解的代码。这类微小的改变是很有帮助的:当阅读代码的人读到排序这部分时,他们就可以在不借助注释的情况下立即推断出将会执行的逻辑。

小贴士

这种通过注入不同的比较函数来改变 sort() 函数工作方式的模式,实际上是策略设计模式的一种表现形式。第 11 章《实现函数式的设计模式》会具体论述。

提供一个排序函数作为参数(典型的函数式编程风格)还有助于解决其他问题,例如:

  • sort() 仅适用于字符串。要对数字进行排序,必须提供一个数字排序函数,如:myNumbers.sort((a,b) => a-b)
  • 如要按给定属性对对象排序,则需要传入一个与该属性值进行比较的函数。如:myPeople.sort((a,b) => a.age - b.age) 可以按年龄升序对人员进行排序。

小贴士

更多 localeCompare() 介绍,请参阅 MDN 官方文档。您可以指定区域规则、大小写字母的排序规则以及是否忽略标点符号等。但请注意:并非所有浏览器都支持所需的额外参数。

这是一个您以前可能用过的简单示例,但它毕竟是一种函数式编程模式。接下来让我们来看看调用 Ajax 时将函数作为参数的更常见用法。

3.2.2. 回调、期约及延续 Callbacks, promises, and continuations

作一等对象传参的函数最常用的示例应该就是回调(callbacks)和期约(promises)了。在 Node 环境下,读取文件是异步完成的:

const fs = require("fs");

fs.readFile("someFile.txt", (err, data) => {
    if (err) {
        console.error(err); // or throw an error, or otherwise handle the problem
    } else {
        console.log(data.toString()); // do something with the data
    }
});

readFile() 需要一个回调函数——本例为一个匿名函数——它将在文件读取操作完成时被调用。

更好的方法是使用 Promise,详细介绍参考 MDN 文档。有了 Promise,当使用更现代的 fetch() 函数执行 Ajax 调用 Web 服务时,可以按以下代码执行一些逻辑:

fetch("some/remote/url")
  .then(data => {
    // Do some work with the returned data
  })
  .catch(error => {
    // Process all errors here
  });

提示

请注意,如果定义了适当的 processData(data)processError(error) 函数,则代码可以像之前提过的那样,精简为 fetch("some/remote/url").then(processData).catch(processError)

最后,还应该考虑使用 async / await,具体用法详见 MDN 文档 async_function 和 await operator。

3.2.3. 连续传递风格 Continuation passing style

前面的代码,在调用一个函数的同时,还传递了另一个在 I/O 操作完成时要执行的函数,可以认为是 连续传递风格CPS,Continuation Passing Style)的一种具体体现。这是一种什么样的编码技术呢?不妨从一个实际问题切入理解:如果禁止使用 return 语句,该怎样编程?

乍一看,这个问题似乎无从下手。然而,通过 将回调传函数递给被调用函数,我们便能寻得解决之道:当该过程准备返回控制权给调用者时,它不会实际返回,而是去调用所传递的回调函数。这么一来,回调函数就为被调用函数提供了延续该操作过程的一种途径,CPS 风格中的“连续”(continuation)就是这么来的。CPS 风格本节不具体展开,留待第九章《函数设计——递归》再进行深入研究。值得一提的是,正如我们将看到的那样,CPS 风格将有助于规避递归中的一个重要限制。

弄清“连续”的具体用法,有时是一件颇具挑战的事,但总归是能够达成的。这种编码方式一个有趣的好处在于,通过指定程序的接续方式,可以打破所有常见的程序控制结构(ifwhilereturn 等等),实现想要的任何控制流程。对于处理过程未必是线性的某些问题而言,这类编码风格将会非常有用。当然,这也可能导致您新发明的某种控制结构,远比想象中使用 GOTO 语句的后果更为糟糕!这种做法的危险如下图所示:

图 3.1 弄乱程序流程,可能会发生什么更糟糕的情况呢?

【图 3.1 弄乱程序流程,可能会发生什么更糟糕的情况呢?】

拓展

这部 XKCD 漫画可以在 这里 在线访问。

此外,可供传递的“连续”体也可以不止一个。就像 Promise 那样,可以提供两个或多个回调逻辑参与传递。顺便说一句,这一特性可用于异常处理领域:如果只是允许一个函数可以抛出一个错误,那么该错误就很可能潜在地返回给调用者,而事实上我们并不希望这样。解决问题的关键在于:提供另一个专门处理报错的回调函数(即不同的连续体),以便在抛出异常时使用(第十二章《构建更好的容器——函数式数据类型》将提出一个基于 monads 的新解决方案):

function doSomething(a, b, c, normalContinuation, errorContinuation) {
  let r = 0;
  // ... do some calculations involving a, b, and c,
  // and store the result in r
    
  // if an error happens, invoke:
  // errorContinuation("description of the error")
    
  // otherwise, invoke:
  // normalContinuation(r)
}

利用 CPS 甚至可以超越 JavaScript 现有的控制结构,但这超出了本书的讨论范围,感兴趣的读者可自行研究。

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

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

相关文章

“Flash闪存”基础 及 “SD NAND Flash”产品的测试

本篇除了对flash闪存进行简单介绍外,另给读者推荐一种我本人也在用的小容量闪存。 自带坏块管理的SD NAND Flash(贴片式TF卡),尺寸小巧,简单易用,兼容性强,稳定可靠,标准SDIO接口&a…

【C++】map 和 set

目录 一 基础概念 1 关联式容器 2 键值对 3 树形结构的关联式容器 二 map 1 概念 2 基础操作 3 使用实列 1 实例一 2 实例二 3 实例三 4 实例四 4 multimap 1 实例一 三 set 1 概念 2 基础操作 3 使用实例 1 实例一 2 实例二 3 实例三 4 multiset 1 实…

SpringBoot使用esayExcel根据模板导出excel

1、依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.3</version></dependency> 2、模板 3、实体类 package com.skybird.iot.addons.productionManagement.qualityTesting…

流程图 LogicFlow

流程图 LogicFlow 官方文档&#xff1a;https://site.logic-flow.cn/tutorial/get-started <script setup> import { onMounted, ref } from vue import { forEach, map, has } from lodash-es import LogicFlow, { ElementState, LogicFlowUtil } from logicflow/core …

字符编码发展史6 — BOM字节序标记

上一篇《字符编码发展史5 — UTF-16和UTF-32》我们讲解了UTF-16和UTF-32编码。本篇我们将继续讲解字符编码中的字节序标记(BOM)。 2.3. 第三个阶段 国际化 2.3.2. Unicode的编码方式 2.3.2.5. BOM 1. 什么是BOM&#xff1f; BOM是Byte Order Mark的缩写&#xff0c;翻译成…

研究生异地报名,需要社保缴费记录,没有社保记录怎么办。

1、户籍在安徽省&#xff0c;在北京工作&#xff0c;想报北京科技大学&#xff1b; 招生简章中没有提社保记录&#xff0c;但是在报名的时候&#xff0c;又出来要求&#xff1a;北京连续6个月的社保记录。这里是指在北京市考试的要求。没有连续社保缴费记录&#xff0c;肯定不能…

Python 与 Pycharm 的简易安装教程,包含Pycharm的修改

一. 官方网站 Python网址&#xff1a;python唯一的官方网址。 Pycharm网址&#xff1a;Pycharm的官方网址。 二. python安装步骤 滑动到红色框内 Downloads 导航栏。 红色框是选择适合自己电脑系统和版本的部分&#xff0c;蓝色框是选择系统的部分&#xff0c;黄色框是版本号。…

【大数据】数据分析之Spark框架介绍

文章目录 概述一、发展历程与背景二、核心特点三、生态系统与组件四、应用场景五、与其他大数据技术的比较 核心概念1. 弹性分布式数据集&#xff08;RDD, Resilient Distributed Dataset&#xff09;2. 转换&#xff08;Transformations&#xff09;和动作&#xff08;Actions…

Rust编程的函数

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust编程与项目实战_夏天又到了的博客-CSDN博客 7.1 函 数 定 义 在Rust中&#xff0c;函数使用fn关键字定义&#xff0c;后跟函数…

how to increase the height of the ps or cdm window

when the line reaches the bottom; directly pull up the top bar of the window after pulling down the bar

【Linux】ComfyUI和SD WebUI之PYTHON环境共享,模型共享,LORA等公共资源共享

需求 一般玩AI绘图都会装ComfyUI和SD WebUI。而且这俩的模型、lora等都是一致的。为了避免空间的浪费&#xff0c;一般会采用共享数据的方式。而且共享的数据可以任意指定分区&#xff0c;这让挂载NAS共享空间成为可能&#xff0c;实现多绘画机ComfyUI和SD WebUI共享资源。 实…

攀爬数据集,约500张 !VOC格式,yolo可直接使用~真实场景特征明显高清图,yolo可直接使用!

攀爬数据集&#xff0c;约500张&#xff01;&#xff01;&#xff01; VOC格式&#xff0c;yolo可直接使用&#xff5e; 真实场景特征明显高清图&#xff0c;yolo可直接使用&#xff01; 攀爬数据集&#xff0c;约500张&#xff01;&#xff01;&#xff01; VOC格式&#xff0…

用GPT-4o打造LLM+OS(10+实用技能),代码开源,指令曝光,科技演示惊艳全场!

目录 前言 LLM操作系统能力概况&#xff08;phidata中前5个已经实现&#xff09;&#xff1a; 可以读取/生成文本 拥有比任何单个人类更全面的知识 可以浏览互联网 可以使用现有的软件基础设施&#xff08;计算器、Python、鼠标/键盘&#xff09; 可以与其他LLMs通信 可…

一文了解,ARM 工业计算机的发展历程

ARM 工业计算机的发展历程主要经历了以下几个阶段&#xff1a; 早期探索阶段&#xff08;20 世纪 80 年代 - 90 年代初&#xff09;&#xff1a; 起源背景&#xff1a;20 世纪 80 年代&#xff0c;计算机工业蓬勃发展&#xff0c;英国的 Acorn 公司在这一时期积极探索芯片技术…

Unity实现自定义图集(四)

以下内容是根据Unity 2020.1.0f1版本进行编写的   在之前的篇章中已经把自定义图集在编辑器上的使用,以及运行时所需的信息都准备好了,接下来就是魔改UGUI的Image组件,使其能够像Image那样运行时如果引用的资源有打自定义图集,则加载对应自定义图集的Texture。 1、思路 …

Centos7通过jengkins实现自动发布和回滚

一、安装jenkins 注&#xff1a;这里不多说哈&#xff0c;百度遍地都是&#xff0c;安装方式不限。 二、jenkins创建项目 注&#xff1a;这里有个坑需要说一下&#xff0c;最开始我使用的是maven构建&#xff0c;但是如果按照我的这套方案会有一个编译死循环的问题&#xff0c;…

【Linux】多进程服务器模型(第十九篇)

目录 一、定义与工作原理 二、特点与优势 三、实现与示例 四、注意事项 多进程服务器模型是一种在服务器端使用的并发处理模型&#xff0c;它允许服务器同时处理多个客户端的请求。以下是关于多进程服务器模型的详细介绍&#xff1a; 一、定义与工作原理 定义&#xff1a;…

抽象类Abstart Class

抽象类其实就是一种不完全的设计图 必须用abstract修饰 模板方法&#xff1a;建议使用final修饰&#xff0c;不能被重写。

提高ROI:低代码平台如何助力企业实现成本效益最大化

引言&#xff1a;成本效益与ROI的重要性 在当今竞争异常激烈的商业环境中&#xff0c;企业面临着前所未有的挑战。如何在有限的资源下&#xff0c;最大化投资回报率&#xff08;ROI&#xff09;&#xff0c;已经成为企业管理者不可忽视的关键课题。ROI不仅仅是衡量投资回报的指…

PROFINET 转 EtherCAT, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 PROFINET 转 EtherCAT GW系列型号 MS-GW31 概述 简介 MS-GW31 是 PROFINET 和 EtherCAT 协议转换网关&#xff0c;为用户提供两…