es-toolkit negate 函数完全指南:一行代码反转布尔谓词,重塑过滤与条件逻辑
2026/9/17 1:57:39 网站建设 项目流程

es-toolkit negate 函数完全指南:一行代码反转布尔谓词,重塑过滤与条件逻辑

【免费下载链接】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

导读

negate是 es-toolkit 在 Function 模块 中提供的高阶函数工具,它接收一个返回布尔值的函数(谓词),并返回一个逻辑结果完全相反的新函数。在数组过滤、条件判断、断言组合等场景中,negate能让你用「正向思维」定义规则,再通过取反复用同一份逻辑,避免重复书写!表达式或冗余的反向谓词。读完本文,你将掌握negate的 API 签名、源码级实现原理、与Array.prototype.filter的搭配技巧,以及它与 lodash 兼容版本negate的行为差异。

一、negate是什么:反转布尔结果的函数工厂

negate是典型的「高阶函数」(higher-order function):它不直接参与业务计算,而是接收一个函数、返回一个新函数。其核心职责在官方文档(英文版、日文版)中被定义为:

创建一个新函数,将「返回 true 或 false 的函数」的返回值反转。

用一个最简单的类型签名即可概括其行为:

const negatedFunc = negate(booleanFunc);

booleanFunc接收任意参数并返回布尔值;negatedFuncbooleanFunc接收完全相同的参数,但返回值永远是!booleanFunc(...)的结果。

适用场景

negate最适合用于反转条件函数或过滤逻辑。例如,你已经写好了一个「判断偶数」的函数,想得到「判断奇数」的函数,无需重写一遍n % 2 !== 0,直接negate(isEven)即可。这种「定义一次、双向复用」的思路在条件组合密集的代码中能显著减少重复与出错概率。

二、快速上手:基本用法与数组过滤实战

从 negate 官方文档 的示例出发,negate的标准导入方式与基本用法如下:

import { negate } from 'es-toolkit/function'; // 基本的な使用法(基本用法) const isEven = (n: number) => n % 2 === 0; const isOdd = negate(isEven); console.log(isEven(2)); // true console.log(isOdd(2)); // false console.log(isEven(3)); // false console.log(isOdd(3)); // true // 配列フィルタリングで使用(用于数组过滤) const numbers = [1, 2, 3, 4, 5, 6]; const evenNumbers = numbers.filter(isEven); console.log(evenNumbers); // [2, 4, 6] const oddNumbers = numbers.filter(negate(isEven)); console.log(oddNumbers); // [1, 3, 5]

上面的示例揭示了negate最典型的实战组合:filternegate天然互补。filter保留谓词返回true的元素,negate负责把谓词反转,于是filter(negate(pred))就等价于「过滤掉满足pred的元素」。

文档还给出了对复杂条件函数取反的示例——反转字符串长度判断:

import { negate } from 'es-toolkit/function'; const isLongString = (str: string) => str.length > 5; const isShortString = negate(isLongString); const words = ['hi', 'hello', 'world', 'javascript']; const longWords = words.filter(isLongString); console.log(longWords); // ['javascript'] const shortWords = words.filter(isShortString); console.log(shortWords); // ['hi', 'hello', 'world']

注意isLongStringisShortString接收的参数(一个字符串)和返回类型(布尔值)完全一致,这正是negate类型保真(type-preserving)的体现:入参签名不变,只翻转返回值

三、API 参考:参数与返回值

negate(func)

项目说明
参数func(类型F一个返回布尔值的函数(谓词函数),F被约束为(...args: any[]) => boolean
返回值(类型F一个新函数:与原函数接收相同参数,但返回相反的布尔值

关键点有二:

  1. 参数透传:返回的新函数与func拥有完全一致的参数列表,调用时原样透传,因此negate不会破坏原有函数的调用约定;
  2. 类型保留:从源码可见negate的返回类型直接复用泛型F,而不是简单地返回(...args: any[]) => boolean。这意味着在 TypeScript 中,const isOdd: (n: number) => boolean = negate(isEven)的类型推断是精确的,不会退化成宽泛的函数类型。

四、源码解析:三行核心实现与 lodash 兼容版的差异

4.1 核心实现(es-toolkit 原生版)

negate的核心实现位于 src/function/negate.ts,全文仅有一个导出函数:

export function negate<F extends (...args: any[]) => boolean>(func: F): F { return ((...args: any[]) => !func(...args)) as F; }

从源码结构可以提炼出三条实现事实:

  1. 闭包捕获:返回的箭头函数通过闭包持有func,并在每次调用时执行!func(...args),用逻辑非(!)对结果取反;
  2. this绑定不传递:原生版返回的是箭头函数,箭头函数不绑定自己的this,因此调用negatedFunc时外层的this不会被传入func。对于纯函数式谓词(如isEven)这毫无影响,但如果你的谓词依赖this,请留意这一点;
  3. 无参数校验:原生版没有对func做类型检查,直接调用!func(...args)。若传入非函数,会在运行时抛出TypeError,类型层面则由泛型约束F extends (...args: any[]) => boolean在编译期拦截。

对应的单元测试位于 src/function/negate.spec.ts,使用 vitest 验证了三个关键行为:

  • negate的返回值是一个函数(typeof negate(() => true) === 'function');
  • 布尔结果被正确反转(negate(() => true)()falsenegate(() => false)()true);
  • filter组合后能正确筛出奇数([1, 2, 3, 4, 5, 6].filter(negate(isEven))等于[1, 3, 5])。

4.2 lodash 兼容版(compat 模块)

es-toolkit 还提供与 lodash 行为对齐的兼容版本,实现在 src/compat/function/negate.ts。对比两者,兼容版存在三处显著差异:

  1. 显式校验:兼容版在调用前检查typeof func !== 'function',不满足时抛出TypeError('Expected a function'),与 lodash 的报错行为一致;
  2. this绑定传递:兼容版返回普通函数(function (this: any, ...args: any[])),并通过func.apply(this, args)把调用时的this透传给原函数,行为更贴近 lodash 语义;
  3. 双重重载签名:兼容版同时导出了基于元组类型T extends any[]的签名与基于函数类型F的签名,为不同调用场景提供更灵活的类型推导。

如果项目从 lodash 迁移且依赖其this传递语义,应优先选用 compat 入口(见下文「如何引用」);如果追求极致的包体积与简单性,原生版是更优选择。

五、组合与边界:negate的进阶用法

5.1 与filtersomeevery等方法的组合

negate不仅适用于数组过滤,凡是以谓词为参数的 API 都能与其组合:

import { negate } from 'es-toolkit/function'; const isEven = (n: number) => n % 2 === 0; const numbers = [1, 2, 3, 4, 5, 6]; // 是否存在奇数 console.log(numbers.some(negate(isEven))); // true // 是否全部为奇数 console.log(numbers.every(negate(isEven))); // false // 找到第一个奇数 console.log(numbers.find(negate(isEven))); // 1 // 第一个奇数的下标 console.log(numbers.findIndex(negate(isEven))); // 0

由于negate返回的函数与原函数签名一致,这些内建数组方法的回调位置都能直接接受negate(...)的产物,无需额外包装。

5.2 链式取反与语义可读性

negate(negate(fn))在逻辑上等价于fn本身,因此一般不需要双重取反。更常见的需求是先组合、后取反,例如把多个条件用&&组合成谓词后再整体取反,negate依然适用,因为它只关心入参函数的布尔返回值,不关心谓词内部逻辑有多复杂。

5.3 注意事项

  • negate期望入参返回「可被!归一化为布尔值」的值;虽然 JavaScript 的!对任意值都成立,但把非布尔返回值(如0''null)传入negate后,取反结果会基于真值(truthiness)语义,而不是字面意义上的「相反值」;
  • 原生版不传递this,若谓词内部使用了this(例如对象方法),建议改用 compat 版或先用bind固定上下文;
  • negate每次调用都会创建新的闭包,在热路径中反复创建会带来微量开销,但现代 JS 引擎对此优化良好,常规业务场景无需顾虑。

六、如何引用:从安装到按需导入

negate随 es-toolkit 的 Function 模块一起发布,可以通过两种路径导入:

// 方式一:按模块导入(推荐,利于 tree shaking) import { negate } from 'es-toolkit/function'; // 方式二:从主入口导入(整个库统一入口) import { negate } from 'es-toolkit';

从 src/index.ts 可以看到,negate经由export * from './function/index.ts'被聚合到主入口;而 src/function/index.ts 则显式export { negate } from './negate.ts'作为模块内导出。因此两种写法均有效,按需导入可让打包器只保留实际用到的代码,契合 es-toolkit 主打的小体积与 tree shaking 特性。

lodash 兼容行为则从 compat 入口引用:

import { negate } from 'es-toolkit/compat';

安装方式与仓库其他功能一致,通过npm install es-toolkit安装后即可使用,完整文档可参考 docs/reference/function/negate.md 及各语言版本(日文版、中文版)。

七、总结

negate是一个「小而美」的高阶函数:核心实现仅一行逻辑非运算,却能在过滤、断言、条件组合等场景中消除大量重复代码。回顾本文要点:

  • 行为:接收布尔谓词,返回参数签名相同、布尔结果相反的新函数;
  • 典型组合filter(negate(pred))即「排除满足 pred 的元素」,some/every/find同理;
  • 实现差异:原生版(src/function/negate.ts)极简且不传this;compat 版(src/compat/function/negate.ts)带参数校验、透传this并贴近 lodash 语义;
  • 可靠性:行为由 src/function/negate.spec.ts 的单元测试覆盖验证。

下次当你写出!somePredicate(x)或被迫定义两个语义相反的谓词时,先想想negate——一行取反,逻辑更清晰。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询