我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

es-toolkit `without` 函数深度解析:从数组中安全剔除指定值的完整指南

es-toolkit `without` 函数深度解析:从数组中安全剔除指定值的完整指南 es-toolkitwithout函数深度解析从数组中安全剔除指定值的完整指南【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkitwithout是 es-toolkit 数组模块中用于从数组中剔除指定值并返回新数组的核心工具函数。本文以官方文档 docs/ja/reference/array/without.md 为骨架结合仓库源码、测试用例与 FP函数式编程变体系统讲解其用法、参数语义、NaN/正负零等边界行为以及底层实现原理帮助你在实际项目中安全、高效地使用它。一、函数概览一行 API专注剔除without接收一个数组和任意数量的待剔除值返回一个不修改原数组的全新数组原数组中所有与指定值相等的元素都会被移除。const filtered without(arr, ...values);它的类型签名见 src/array/without.tsexport function withoutT(array: readonly T[], ...values: T[]): T[]从源码可以看到without的实现极其轻量——它直接复用了同目录下的difference函数export function withoutT(array: readonly T[], ...values: T[]): T[] { return difference(array, values); }也就是说without(arr, v1, v2, ...)在语义上等价于求arr与[v1, v2, ...]的差集。这一设计让整个函数保持单一职责difference负责集合差集的通用逻辑without则提供更贴合剔除场景的调用体验。二、基本使用数值与字符串数组的剔除官方文档给出的两个示例清晰地展示了典型用法原文见 docs/ja/reference/array/without.mdimport { without } from es-toolkit/array; // 数值数组剔除 2 和 4 without([1, 2, 3, 4, 5], 2, 4); // Returns: [1, 3, 5] // 字符串数组剔除所有 a注意 a 出现多次时全部移除 without([a, b, c, a], a); // Returns: [b, c]需要注意几点实战细节剔除是全量的只要元素与任一指定值相等无论出现多少次都会被移除如上例中两个a都被删掉。原数组保持不变与 lodash 中会原地修改数组的_.pull不同详见 src/compat/array/pull.ts 中 Unlike_.without, this method mutatesarray 的注释without始终返回新数组符合不可变数据流的习惯。没有匹配时原样返回等价数组如果指定值都不在数组中返回的数组内容与原数组一致测试用例见 src/array/without.spec.ts。三、参数与返回值详解根据文档docs/ja/reference/array/without.mdwithout的接口约定如下参数参数类型说明arrreadonly T[]需要从中剔除值的源数组只读即可不会被修改...valuesArrayT可变参数一个或多个要从数组中删除的值返回值类型T[]一个剔除了所有指定值的新数组。边界行为有测试佐证场景输入输出依据空数组without([], 1, 2, 3)[]without.spec.ts不传剔除值without([1, 2, 3])[1, 2, 3]without.spec.ts剔除值不在数组中without([1, 2, 3], 4, 5)[1, 2, 3]without.spec.ts混合类型1与1视为不同值without([1, 1, 2, 2], 1, 2)[1, 2]without.spec.ts混合类型示例特别值得注意由于比较基于严格相等语义数字1与字符串1是不同元素因此1被剔除时字符串1会保留。四、NaN与正负零SameValueZero 相等语义without的文档专门强调了一点NaN值也能被正确处理。import { without } from es-toolkit/array; without([1, NaN, 3, NaN, 5], NaN); // Returns: [1, 3, 5]这看起来平凡实际却不简单。因为NaN NaN在 JavaScript 中为false如果实现时直接用indexOf或比较NaN将永远无法被剔除。es-toolkit 之所以能做到是因为底层比较采用了 ECMAScript 规范中的 SameValueZero 的 JSDoc 中。SameValueZero 的关键特性NaN与自身相等 →NaN可以被正常剔除0与-0相等 → 用0剔除时-0也会一并被移除。这一语义在difference的实现中体现得淋漓尽致——它用Set承载待剔除值而Set的成员判定正是基于 SameValueZero见 src/array/difference.tsexport function differenceT(firstArr: readonly T[], secondArr: readonly T[]): T[] { const secondSet new Set(secondArr); return firstArr.filter(item !secondSet.has(item)); }因此without天然继承了Set.has的相等规则。测试 src/array/without.spec.ts 对此有明确覆盖// NaN 可被剔除 expect(without([NaN, 1, 2, NaN, 3], NaN)).toEqual([1, 2, 3]); // 0 与 -0 视为相等用 0 或 -0 剔除效果一致 expect(without([0, -0, 1, 2], 0)).toEqual([1, 2]); expect(without([0, -0, 1, 2], -0)).toEqual([1, 2]);五、性能设计Set查询与线性时间复杂度从实现层面看difference的时间复杂度为 O(n m)n 为源数组长度m 为剔除值个数先用待剔除值构造SetO(m)再遍历源数组逐项执行Set.has查询每次 O(1)。const secondSet new Set(secondArr); // 预处理O(m) return firstArr.filter(item !secondSet.has(item)); // 遍历O(n)每次查询 O(1)对比对每个元素逐一indexOf扫描剔除值列表的朴素实现O(n × m)Set方案在大数据量下优势明显。这是without在保持 API 简洁的同时具备良好性能的关键。六、FP 变体柯里化与惰性求值如果你的项目使用函数式编程风格es-toolkit 还提供了es-toolkit/fp下的withoutsrc/fp/array/without.ts它把参数顺序反转并柯里化先传入待剔除值返回一个过滤函数再应用于数组。import { pipe, without } from es-toolkit/fp; pipe([1, 2, 3, 2], without(2)); // [1, 3]该变体的实现细节见 src/fp/array/without.ts先根据剔除值构造valueSet供惰性分支复用withoutEager回退到基础版withoutToolkit做急切求值withoutLazy通过createLazyFunction实现惰性过滤——在pipe中只有值不在valueSet中时才emit放行最终用combineEagerAndLazyFunctions合并两种模式。惰性求值的价值在于当pipe管道中存在take、head这类提前终止的操作时FP 变体测试见 src/fp/array/without.spec.tswithout可以配合take只处理必要的前几个元素而无需遍历整个数组从而提升整体执行效率。import { pipe, map, without, take } from es-toolkit/fp; // without(1) 配合 take(2)无需扫描完整个数组即可提前返回 pipe([1, 2, 3, 4], map(spy), without(1), take(2)); // [2, 3]七、compat 变体lodash 兼容与类数组支持在es-toolkit/compatlodash 兼容层中without的行为进一步扩展以对齐 lodashsrc/compat/array/without.tsexport function withoutT(array: ArrayLikeT | null | undefined, ...values: T[]): T[]相比基础版compat 版新增了三个能力接受类数组对象ArrayLike字符串、arguments等带length与索引访问的对象也可作为输入对null/undefined宽容输入非法时返回[]而不是抛异常isArrayLikeObject判空对应测试 src/compat/array/without.spec.ts零值归一化通过内部工具normalizeZero将-0统一规范为0后放入结果保证输出值形式一致。此外兼容层还支持对象引用剔除——按引用相等移除指定对象如without(array, object1)只移除引用相同的那个对象测试见 src/compat/array/without.spec.ts。八、与其他移除类 API 的对比es-toolkit 提供了多个移除元素相关函数选择时注意区分函数行为是否修改原数组实现位置without按值剔除返回新数组否src/array/without.tsdifference两个数组取差集返回新数组否src/array/difference.tspull按值剔除原地修改数组是src/compat/array/pull.ts从源码看without与pull的注释相互呼应without走不可变、返回新数组路线而pull走原地变更、性能优先路线src/compat/array/pull.ts 明确注明 Unlike_.without, this method mutatesarray。若你的代码依赖原数组保持不变如 React state 更新应优先使用without若对内存敏感且原数组可以丢弃pull也是一种选择。九、快速上手without通过 src/array/index.ts 统一导出可按需引入import { without } from es-toolkit/array; // 剔除多个指定值 const result without([1, 2, 3, 4, 5], 2, 4); // [1, 3, 5] // 配合 Set 做黑名单过滤 const blacklist new Set([2, 4]); const data [1, 2, 3, 4, 5]; const filtered without(data, ...blacklist); // [1, 3, 5] // 函数式管道需要 es-toolkit/fp import { pipe, without } from es-toolkit/fp; pipe(data, without(...blacklist)); // [1, 3, 5]总结without虽是一行式 API背后却凝聚了多项工程决策以differenceSet实现 O(n m) 的高效剔除、借助 SameValueZero 语义正确处理NaN与±0、保持原数组不可变、并提供 FP 惰性与 compat 兼容两种变体。掌握它的参数约定与边界行为能让你在数据清洗、黑名单过滤、状态更新等场景中写出既简洁又健壮的代码。想深入了解实现与测试可以继续阅读基础实现src/array/without.ts、src/array/difference.ts单元测试src/array/without.spec.tsFP 变体src/fp/array/without.tslodash 兼容变体src/compat/array/without.ts官方文档日语docs/ja/reference/array/without.md【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表