news 2026/9/16 17:13:37

es-toolkit 的 cond 函数:用函数式风格编排多条件分支的 Lodash 兼容实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
es-toolkit 的 cond 函数:用函数式风格编排多条件分支的 Lodash 兼容实现

es-toolkit 的 cond 函数:用函数式风格编排多条件分支的 Lodash 兼容实现

【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit

导读

cond是 es-toolkit 兼容层(es-toolkit/compat)提供的一个工具函数:它接收一组"条件-动作"函数对,返回一个新的复合函数,按顺序逐个求值条件,命中第一个为真的条件时执行其对应的动作函数并返回结果,若全部条件均为假则返回undefined。本文以 cond 官方文档 为主体,结合 cond 源码实现、单元测试 与 性能基准,讲清它的用法、类型签名、底层工作原理、与 Lodash 的兼容行为,以及官方文档明确提示的性能注意点,帮助你判断"何时该用、何时不该用"。

一、核心概念:什么是cond

cond(pairs)接收一个由[predicate, func]二元组组成的数组,返回一个新函数。调用该新函数时,它会把收到的参数依次交给每个predicate(条件谓词)求值,一旦某个谓词返回真值,就调用同组的func(动作函数),并直接返回其结果;如果所有谓词都为假,则返回undefined

const conditionFunction = cond(pairs);

在函数式风格中,cond相当于一个"可组合的 if-else 链":它把离散的条件分支封装成一个可传递、可复用的值,让调用方无需关心分支细节,只需调用conditionFunction(...args)

注意:es-toolkit 的cond只存在于es-toolkit/compat兼容入口中,使用前需从该路径导入:import { cond } from 'es-toolkit/compat';

二、基础用法:按顺序匹配条件

官方文档给出的第一个场景是"按数值区间返回不同标签"。cond从数组第一个二元组开始依次判断,命中即返回,因此条件的书写顺序直接决定优先级:

import { cond } from 'es-toolkit/compat'; // Basic usage const getValue = cond([ [x => x > 10, x => 'big'], [x => x > 5, x => 'medium'], [x => x > 0, x => 'small'], [() => true, () => 'zero or negative'], ]); console.log(getValue(15)); // "big" console.log(getValue(8)); // "medium" console.log(getValue(3)); // "small" console.log(getValue(-1)); // "zero or negative"

这里最后一个[() => true, () => 'zero or negative']是一个"兜底分支":由于() => true恒为真,任何在前面分支中未命中的输入都会落入此处,等效于 else 子句。这种"前分支写具体条件、末位写恒真兜底"是使用cond的典型模式。

关键语义:只执行第一个命中的分支

cond与普通 if-else 链一样是"短路"的——一旦某个条件为真,后续分支不再求值,且只有第一个命中的动作函数会被执行。官方文档用下面这个例子验证这一点:

import { cond } from 'es-toolkit/compat'; const checkValue = cond([ [x => x > 10, x => 'greater than 10'], [x => x < 5, x => 'less than 5'], ]); console.log(checkValue(15)); // "greater than 10" console.log(checkValue(3)); // "less than 5" console.log(checkValue(7)); // undefined (no matching condition)

当输入7时,两个条件都不满足,函数返回undefined。这一行为在 cond.spec.ts 中有专门测试:cond([[stubFalse, stubA]])传入任意对象都会得到undefined,因为stubFalse恒为假。

三、进阶用法:对象模式匹配

cond的谓词并不仅限于数值比较,它接受任意谓词函数,因此天然适合做对象字段的模式匹配——把"角色判断 + 字符串格式化"这类逻辑收敛进一张"规则表":

import { cond } from 'es-toolkit/compat'; const processUser = cond([ [user => user.role === 'admin', user => `Admin: ${user.name}`], [user => user.role === 'user', user => `User: ${user.name}`], [user => user.role === 'guest', user => `Guest: ${user.name}`], [() => true, () => 'Unknown role'], ]); console.log(processUser({ name: 'John', role: 'admin' })); // "Admin: John" console.log(processUser({ name: 'Jane', role: 'user' })); // "User: Jane"

这种写法的价值在于:规则表本身是一个可配置、可动态构建的数据结构,你可以把pairs数组存起来、按需重组或由外部配置驱动,比硬编码的 if-else 更灵活;代价则是可读性与性能的取舍(详见第五节)。

四、类型签名与参数说明

根据 cond 源码,该函数提供两组重载签名:

// 无参调用形式(谓词与动作都不接收输入参数) export function cond<R>(pairs: Array<[truthy: () => boolean, falsey: () => R]>): () => R; // 接收一个输入值的形式(谓词与动作共享同一个输入) export function cond<T, R>(pairs: Array<[truthy: (val: T) => boolean, falsey: (val: T) => R]>): (val: T) => R;
项目说明
pairsArray<[predicate, func]>,由"条件函数 + 动作函数"组成的二元组数组
predicate条件函数,接收调用参数,返回布尔值(真值即命中)
func动作函数,命中对应条件后执行,其返回值作为整个调用的结果
返回值(...args: any[]) => unknown,一个新的复合函数;全部条件不命中时返回undefined

值得注意的底层细节:虽然重载签名限制为"无参"或"单参",但实际实现(export function cond(pairs: any[][]): (...args: any[]) => unknown)会对任意数量的参数求值,并且谓词与动作函数会收到完全相同的参数。这一点由 cond.spec.ts 验证:调用resultFunc('a', 'b', 'c')时,谓词和动作函数都收到['a', 'b', 'c']

参数校验与this绑定

实现中还有两个容易被忽略的行为,均有测试佐证:

  1. 动作函数必须为函数类型:构造时若pair[1]不是函数(如falsetrue),会直接抛出TypeError('Expected a function')。见 cond.ts 与对应测试 cond.spec.ts。校验在"构造阶段"进行,而不是调用阶段。
  2. this透传:生成的复合函数会保留调用方的this上下文,谓词与动作函数均通过predicate.apply(this, args)/func.apply(this, args)调用。测试 cond.spec.ts 展示了一个利用this取属性值的场景。

谓词简写:iteratee的自动转换

这是cond与 Lodash 兼容的关键点之一。源码中每个谓词都会经过iteratee(predicate)归一化处理(见 cond.ts),而 iteratee 实现 支持以下输入形式:

  • 函数:原样返回,直接作为谓词;
  • 属性名(如'c'):转换为"取该属性"的函数,真值即命中;
  • 属性-值二元组(如['b', 1]):转换为"属性值是否等于给定值"的判断(matchesProperty);
  • 部分对象(如{ a: 1 }):转换为"对象是否匹配该部分对象"的判断(matches)。

因此你可以把文档示例中的函数谓词写成简写形式,测试 cond.spec.ts 验证了这种写法与函数写法行为完全一致:

const resultFunc = cond([ [{ a: 1 }, () => 'a'], // 对象简写 → matches({ a: 1 }) [['b', 1], () => 'b'], // 属性-值简写 → matchesProperty('b', 1) ['c', () => 'c'], // 属性名简写 → property('c') ]);

五、性能注意:官方明确建议优先使用 if-else / switch

cond文档开头带有一段醒目的 warning,这是本文最需要认真对待的工程建议:

由于复杂的 iteratee 处理、数组变换和函数校验,cond函数运行较慢。建议改用更快、更清晰的 if-else 或 switch 语句。

从源码看,这个警告并非空穴来风,性能开销主要来自三处(见 cond.ts):

  1. 构造阶段的开销pairs.map(...)对每一对执行isFunction校验并调用iteratee做谓词归一化,生成新的processedPairs数组;
  2. 调用阶段的间接层:每次调用都要经predicate.apply/func.apply动态分发,且谓词是经过包装的 iteratee,比直接写if (x > 10)多一层函数调用;
  3. 数组遍历:条件匹配是线性扫描for循环,最坏情况(无命中)需要遍历全部二元组。

仓库 benchmarks/performance/cond.bench.ts 中同时用es-toolkit/compat/condlodash/cond各跑 100ms 的基准用例(覆盖命中第一个、第二个、第三个分支及全部未命中四种输入),如果你关心与 Lodash 的量化差异,可以在本仓库运行性能基准自行对比。

因此,实践上的选择标准是:

  • 热路径、性能敏感、分支逻辑稳定:直接用 if-else 或 switch,可读性和性能都更好;
  • 规则表可配置、需要动态组装分支、或追求函数式组合cond是合理选择,但要意识到它带来的间接调用开销;
  • 兜底分支:习惯上用() => truestubTrue作为最后一个二元组,模拟 else 语义,避免返回意外的undefined

六、兼容定位与测试保障

cond是 es-toolkit 的 Lodash 兼容实现,从 compat 入口 导出,与 lodash 的_.cond保持一致的语义:条件按顺序求值、只执行第一个命中分支、全不命中返回undefined、支持matches/matchesProperty/property等谓词简写、动作函数非函数时抛TypeError。上述行为在 cond.spec.ts 中共有 6 组用例覆盖(条件组合、参数透传、谓词简写、无命中返回 undefined、TypeError 校验、this 绑定),可作为迁移到 es-toolkit 时的行为对照清单。

总结

cond为函数式风格的多条件分支提供了一种紧凑、可组合、可配置的写法,支持函数谓词与 Lodash 风格的简写谓词,且严格遵循"顺序匹配、短路执行、全不命中返回 undefined"的语义。但在使用前请记住文档中的明确警告:它比 if-else / switch 更慢、间接层更多,适合规则表驱动的场景,而不适合性能敏感的冷热路径。理解 cond.ts 的实现细节(iteratee 归一化、构造期校验、this 透传)能帮助你在兼容性与性能之间做出正确的工程取舍。

【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 17:12:24

Title and authors of the Paper:

Title and authors of the Paper: 【免费下载链接】LifeOS ⛰️ The Life Operating System — an intent engineering platform that moves you from your current state to your ideal state, in life and work. 项目地址: https://gitcode.com/GitHub_Trending/pe/LifeOS …

作者头像 李华
网站建设 2026/9/16 17:10:43

LunaTV 配置订阅教程:填入一个 URL,播放源自动同步

LunaTV 配置订阅教程&#xff1a;填入一个 URL&#xff0c;播放源自动同步 【免费下载链接】LunaTV 本项目采用 CC BY-NC-SA 协议&#xff0c;禁止任何商业化行为&#xff0c;任何衍生项目必须保留本项目地址并以相同协议开源 项目地址: https://gitcode.com/GitHub_Trending…

作者头像 李华
网站建设 2026/9/16 17:04:05

单片机+Proteus实现风光互补路灯智能控制系统设计

简介&#xff1a;基于51单片机的风光互补路灯智能控制系统设计资源&#xff0c;面向单片机学习者、课程设计与毕业设计人群&#xff0c;提供从原理图、仿真到源码的完整方案。系统以51单片机为核心&#xff0c;采用LCD1602实时显示太阳能/风力两路电压电流及路灯状态&#xff0…

作者头像 李华
网站建设 2026/9/16 17:03:55

Java Web投票系统实战:Servlet+JSP+JDBC完整部署与代码级调试

简介&#xff1a;本资源是一套面向高校Web开发课程设计的Java Web投票系统完整实现&#xff0c;适用于Java Web初学者巩固JSP、Servlet及数据库交互技能&#xff0c;解决课程实践中的典型MVC架构落地问题。压缩包共14个文件&#xff0c;含9个JSP页面&#xff08;如index.jsp用户…

作者头像 李华