ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

es-toolkit compat 深度解析 matchesProperty:生成属性值匹配函数,高效实现数组过滤与对象搜索

es-toolkit compat 深度解析 matchesProperty:生成属性值匹配函数,高效实现数组过滤与对象搜索 es-toolkit compat 深度解析 matchesProperty生成属性值匹配函数高效实现数组过滤与对象搜索【免费下载链接】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-toolkitmatchesProperty是 es-toolkit 的 Lodash 兼容层es-toolkit/compat中提供的一个谓词工厂函数它接收一个属性路径和一个目标值返回一个「判定函数」。该判定函数在后续对对象做过滤filter、查找find、断言every/some等操作时反复复用是 Lodash 迁移场景下_.matchesProperty的直接对应物。读完本文你将掌握matchesProperty的完整 API 语义、各种路径形式的取值规则、底层gethasisMatch的匹配实现原理以及它与iteratee、matches等 compat 生态函数的协作关系。一、API 概览与基本形态matchesProperty的核心形态是一个「两参数进、一函数出」的工厂import { matchesProperty } from es-toolkit/compat; const checker matchesProperty(path, value); // checker: (target: unknown) boolean其 TypeScript 声明见 src/compat/predicate/matchesProperty.ts提供多重签名核心约束为property: PropertyPath—— 属性路径类型定义在 src/compat/_internal/PropertyPath.ts本质是ManyPropertyKey即单个PropertyKey或PropertyKey[]source: T—— 用于与目标属性值比较的值声明上为unknown实际可以是任意类型原始值、对象、数组、Map、Set、函数等返回值为(target: V) boolean—— 一个判定函数接收被检测对象返回该对象在指定路径上的属性值是否与source匹配。二、完整用法四类典型匹配场景以下示例完整继承自官方文档 docs/compat/reference/predicate/matchesProperty.md日文版见 docs/ja/compat/reference/predicate/matchesProperty.md覆盖了简单属性、嵌套数组路径、点号深层路径与复杂对象匹配四个层次。2.1 简单属性匹配import { matchesProperty } from es-toolkit/compat; // 简单属性检查 const checkName matchesProperty(name, Alice); const users [ { name: Alice, age: 25 }, { name: Bob, age: 30 }, { name: Alice, age: 35 }, ]; const aliceUsers users.filter(checkName); // [{ name: Alice, age: 25 }, { name: Alice, age: 35 }]返回的判定函数可以直接作为Array.prototype.filter的回调无需再写(user) user.name Alice这类箭头函数样板代码。2.2 嵌套属性匹配数组路径// 嵌套属性检查数组路径 const checkCity matchesProperty([address, city], Seoul); const profiles [ { name: Kim, address: { city: Seoul, district: Gangnam } }, { name: Lee, address: { city: Busan, district: Haeundae } }, { name: Park, address: { city: Seoul, district: Mapo } }, ]; const seoulUsers profiles.filter(checkCity); // [{ name: Kim, address: { city: Seoul, district: Gangnam } }, // { name: Park, address: { city: Seoul, district: Mapo } }]2.3 点号表示的深层路径// 深层路径以字符串表达 const checkScore matchesProperty(stats.game.score, 100); const players [ { name: Player1, stats: { game: { score: 100, level: 5 } } }, { name: Player2, stats: { game: { score: 95, level: 4 } } }, { name: Player3, stats: { game: { score: 100, level: 6 } } }, ]; const perfectScorers players.filter(checkScore); // [{ name: Player1, stats: { game: { score: 100, level: 5 } } }, // { name: Player3, stats: { game: { score: 100, level: 6 } } }]2.4 复杂对象作为匹配值// 与复杂对象匹配 const checkRole matchesProperty(role, { type: admin, permissions: [read, write] }); const accounts [ { user: Alice, role: { type: admin, permissions: [read, write] } }, { user: Bob, role: { type: user, permissions: [read] } }, { user: Charlie, role: { type: admin, permissions: [read, write] } }, ]; const admins accounts.filter(checkRole); // [{ user: Alice, role: { type: admin, permissions: [read, write] } }, // { user: Charlie, role: { type: admin, permissions: [read, write] } }]需要特别理解 2.4 的匹配语义source对象{ type: admin, permissions: [...] }做的是部分匹配partial match——目标对象只需包含 source 中声明的那些键且值匹配即可多出的键不影响结果。这正是底层isMatch的行为下文源码分析会展开。三、参数与返回值参数参数类型说明propertyPropertyKey \| PropertyKey[]要检查的属性路径。可以是字符串、数组或点号分隔的路径如stats.game.score与[stats, game, score]等价。也支持数字数组下标等PropertyKey形式。sourceunknown用于与属性值比较的值。原始值走严格相等语义-0与0视为相等对象/数组/Map/Set 走isMatch的部分匹配语义。返回值((target: unknown) boolean)返回一个判定函数。对给定的目标对象若其指定路径上的属性值与source匹配则返回true否则返回false。四、源码级实现解析实现位于 src/compat/predicate/matchesProperty.ts全文不足百行逻辑可以拆成三段来看路径规范化、source 快照、逐次判定。4.1 路径规范化处理-0与数字键switch (typeof property) { case object: { if (Object.is(property?.valueOf(), -0)) { property -0; } break; } case number: { property toKey(property); break; } }当property是对象包装的数字如Object(-0)时直接转成字符串键-0避免 JS 对象属性名把-0归一化为0导致的误判当property是数字时走 toKey 做字符串化toKey内部同样先检查Object.is(value?.valueOf?.(), -0)返回-0其余情况退化为String(value)。测试文件 src/compat/predicate/matchesProperty.spec.ts 中的should preserve the sign of 0用例会验证{ -0: a }与{ 0: b }两个对象在-0、Object(-0)、0、Object(0)四种键形式下都能得到正确区分保证了对 Lodash 行为的高度兼容。4.2 source 快照cloneDeep 保证「创建时语义」source cloneDeep(source);创建判定函数时source会被 cloneDeep 深拷贝一次。这意味着判定函数捕获的是创建时刻的值快照之后你再修改传入的source对象已创建的判定函数行为不会变化。spec 中的should not change behavior if srcValue is modifiedspec 第 404 行起专门验证了这一点——修改source后旧判定函数仍按快照匹配且不再匹配被修改后的原对象。4.3 判定逻辑get has isMatch 三分支return function (target?: unknown) { const result get(target, property as PropertyKey | PropertyKey[]); if (result undefined) { return has(target, property as PropertyKey | PropertyKey[]); } if (source undefined) { return result undefined; } return isMatch(result, source as object); };判定函数被调用时的完整决策链取路径值调用 get 沿property路径取target上的值。get对null/undefined的目标直接返回默认值即undefined因此对 nullish 目标调用判定函数安全地得到falsespec 中should return false when object is nullish系列用例覆盖了null、undefined以及省略参数的情况。路径值不存在result undefined退化为has(target, path)的存在性检查。这一分支使matchesProperty(b, undefined)在「键b存在且值为undefined」时返回true在「键b根本不存在」时返回false——spec 中should match undefined values与should match undefined values of nested objects两个用例正是验证这组行为。source 为 undefined显式匹配「路径值本身就是 undefined」。其余情况交给 isMatch 做结构化匹配见下一节。另外值得注意的一个细节是「键优先于路径」当对象上字面存在a.b这个键时matchesProperty(a.b, ...)匹配的是这个键本身而不是去解析a.b深层路径。spec 中的should match a key over a path用例确认了{ a.b: 1, a: { b: 2 } }场景下matchesProperty(a.b, 1)为true。这一优先级由get内部「先直取object[path]取不到且isDeepKey(path)才递归走toPath解析」的实现决定见 get.ts 第 460-477 行。五、isMatch 部分匹配语义深潜isMatch自身只是一行委托src/compat/predicate/isMatch.ts 中isMatch(target, source)等价于isMatchWith(target, source, () undefined)真正的匹配规则在 isMatchWith.ts 中。结合该文件的 JSDoc 与实现matchesProperty中source的匹配语义可以归纳为对象source 的每个自有键都必须在 target 中存在且值匹配部分匹配target 可以有多余键嵌套层级上对象型 source 模式只匹配普通对象形态的 target源码通过getTag(target)与objectTag/argumentsTag比对实现数组source 数组的每个元素都能在 target 数组中找到即可不要求顺序也不要求 target 长度一致部分匹配。例如matchesProperty(a, [d])能匹配{ a: [c, d] }但匹配不了{ a: [b] }含重复元素时要求重复次数也够matchesProperty(a, [2, 2])只能匹配{ a: [2, 2] }而不能匹配{ a: [1, 2] }对应 spec 中should partial match arrays系列用例Map / Set同样是部分匹配空 Map/Set 的 source 匹配任意目标source.size 0直接返回true见 isMatchWith.ts 第 287-289 行函数默认按引用相等比较若函数上挂有自有属性则转为按其属性做对象比较spec 中should work with a function for srcValue验证原始值严格相等语义特例是-0与0视为相等should match -0 as 0用例空容器空数组、空对象、空 Map/Set 作为 source 时匹配任意目标should return true when comparing a srcValue of empty arrays and objects用例。理解这套语义的关键结论是matchesProperty的第二个参数不是一个简单的「相等值」而是一个匹配模式。把它用于filter时实际上是在做「属性值包含某个模式」的筛选。六、在 compat 生态中的位置iteratee 与数组函数从源码结构看matchesProperty并非孤立存在而是 compat 谓词家族的核心构件之一iteratee的数组简写分支src/compat/util/iteratee.ts 中当传入「长度为 2 的数组」时直接构造matchesProperty(value[0], value[1])case object: { if (Array.isArray(value) value.length 2) { return matchesProperty(value[0], value[1]); } return matches(value); }也就是说 Lodash 惯用的_.filter(users, [name, Alice])简写在 es-toolkit compat 中就是经由iteratee落到matchesProperty实现的。字符串输入则走property普通对象输入则走matchessrc/compat/predicate/matches.ts三者分工清晰。与matches/property的对比matches对整个对象做部分匹配无路径参数property提取属性值本身返回的不是布尔判定matchesProperty则是「路径 值 → 布尔判定」的中间形态恰好覆盖 Lodash 同名 API 的语义。compat 数组函数的广泛复用源码检索可见src/compat/array/下findLastIndex.ts、dropRightWhile.ts、some.ts、every.ts等数组函数的实现及filter.spec.ts、find.spec.ts等测试均引用matchesProperty从源码结构看这些支持谓词简写函数 / 属性名 /[属性, 值]对的 compat 数组方法其简写解析最终都汇入本文所述的matchesProperty/matches/property三件套。七、边界行为速查以测试为准以下行为均由 src/compat/predicate/matchesProperty.spec.ts 中的用例确认可作为生产使用时的行为契约场景行为目标为null/undefined/未传参返回false不抛异常路径中段缺失如{}上取a[1].b.c返回false数组下标路径matchesProperty(1, 2)作用于[1, 2, 3]返回true1与[1]两种写法均可数字键强制字符串化matchesProperty(null, ...)null/undefined/自定义toString的函数/{}均被转成对应字符串键null、undefined、fn、[object Object]源值对象带继承属性仅按自有键匹配原型上的键不参与should not match by inherited srcValue properties目标属性对象带继承属性继承来的字符串键属性可参与匹配should match inherited string keyed srcValue properties函数作为 source引用相同为true不同函数即使「长得一样」也是false布尔值精确匹配matchesProperty(a, false)严格区分true/false不做 truthy 折叠八、实践建议替代 Lodash 迁移时import { matchesProperty } from es-toolkit/compat与_.matchesProperty(path, value)语义对齐且与filter/find/every等 compat 数组函数组合方式不变性能取向判定函数创建时只深拷贝一次source之后每次调用只做一次路径读取加匹配适合「一个模式、大量对象」的过滤场景避免在filter回调里反复构造模式对象注意部分匹配若你期望「属性值必须与 source 完全相等不多不少而非部分匹配」应改用普通比较或isEqual见 docs/compat/reference/predicate/isEqual.md风格的判定matchesProperty的对象/数组 source 是模式匹配而非全等判断undefined 的两种写法显式传matchesProperty(b, undefined)才能区分「键存在但值为 undefined」与「键不存在」依赖这一区分时不要省略第二个参数函数签名要求两个参数。实现入口 src/compat/predicate/matchesProperty.ts、完整测试 src/compat/predicate/matchesProperty.spec.ts、匹配引擎 src/compat/predicate/isMatchWith.ts 与路径工具 src/compat/object/get.ts、src/compat/_internal/toKey.ts 均可在当前仓库中直接查阅用于进一步核对本文所述的每一条行为。【免费下载链接】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),仅供参考
返回列表