ARTICLE DETAIL

资讯详情

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

jstips 实战:用 pick 与 reject 精准挑选和剔除对象属性

jstips 实战:用 pick 与 reject 精准挑选和剔除对象属性 教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载导读在 jstips 系列的第 70 期技巧中作者 loverajoel 给出了一个非常实用的场景当我们面对一条数据库表记录以对象数组形式存在时往往只需要取出其中的少数几个字段供某个函数使用——这就是对象的白名单pick与黑名单reject操作。读完本文你将掌握两种不依赖 Lodash 等工具库的纯 JavaScript 实现思路理解map/reduce/Object.assign/in运算符在其中的协作方式并看到如何把它们推广到批量数据与现代 ES 语法中。pick按白名单挑选对象属性原文档给出的核心场景是将数据库表的行row视作对象用函数只select出需要的字段function pick(obj, keys) { return keys.map(k k in obj ? {[k]: obj[k]} : {}) .reduce((res, o) Object.assign(res, o), {}); } const row { accounts.id: 1, client.name: John Doe, bank.code: MDAKW213 }; const table [ row, {accounts.id: 3, client.name: Steve Doe, bank.code: STV12JB} ]; pick(row, [client.name]); // Get client name table.map(row pick(row, [client.name])); // Get a list of client namespick 的小把戏map 之后 reducepick 的实现里有一段巧妙的流水线原文档称之为 skulduggery第一步keys.map(...)对传入的键名数组做映射每次迭代返回一个只含当前键的对象{[k]: obj[k]}如果目标对象中没有该键用k in obj判断则返回空对象{}以此跳过不存在的属性。例如对row挑选[client.name]这一步会得到[{ client.name: John Doe }]如果传入一个不存在的键如[nope]则得到[{}]。第二步.reduce((res, o) Object.assign(res, o), {})把这些单属性对象依次用Object.assign合并进累加器res初始值为{}最终得到只含目标键的浅拷贝对象。两步合起来本质是先逐键抽取、再逐个合并。因为对象字面量的键用{[k]: obj[k]}这种计算属性名computed property names语法动态生成所以这段代码可以正确处理accounts.id、client.name这类带点号的字符串键——它们被当作普通字符串属性名处理而不是嵌套路径。为什么用k in obj而不是obj[k]这里的关键判断用的是in运算符而非真值判断。jstips 第 10 期 Check if a property is in a Object 专门讨论过两者差异k in obj会沿着原型链检查而obj.hasOwnProperty(k)只检查自有属性直接写obj[k]则会把值为0、、null、false等 falsy 的属性误判为不存在。使用in可以保证值本身是 falsy 但键确实存在的属性也能被正确挑选出来这正是 pick 作为通用工具函数的健壮性所在。批量挑选table.map 组合由于 pick 返回的是新对象、不修改原对象它可以安全地用于批量处理table.map(row pick(row, [client.name])); // [{client.name: John Doe}, {client.name: Steve Doe}]这正好对应了数据库表 SELECT 字段的语义map遍历每一行pick挑出投影字段组合成一个精简的新表。同样的手法也可以用于将 API 返回的大对象裁剪成前端页面真正需要的字段子集避免把冗余数据透传给组件或下游接口。reject按黑名单剔除对象属性如果想反过来剔除某些属性函数需要换个写法。原文档给出了两个版本function reject(obj, keys) { return Object.keys(obj) .filter(k !keys.includes(k)) .map(k Object.assign({}, {[k]: obj[k]})) .reduce((res, o) Object.assign(res, o), {}); } // or, reusing pick function reject(obj, keys) { const vkeys Object.keys(obj) .filter(k !keys.includes(k)); return pick(obj, vkeys); } reject({a: 2, b: 3, c: 4}, [a, b]); // {c: 4}版本一filter 出保留键再逐键拷贝第一个版本先Object.keys(obj)拿到对象的所有自有键用.filter(k !keys.includes(k))把黑名单中的键过滤掉剩下的键再像 pick 一样逐个转成单属性对象并合并。注意它写的是Object.assign({}, {[k]: obj[k]})——外层包了一个空对象作为目标这同样是为了避免直接修改原始对象保证纯函数语义。版本二复用 pick代码更简洁第二个版本把过滤键名这一步单独抽出来得到白名单数组vkeys后直接交给现成的 pick 处理function reject(obj, keys) { const vkeys Object.keys(obj) .filter(k !keys.includes(k)); return pick(obj, vkeys); }这个版本代码量更少、职责更清晰reject 只负责算出该保留哪些键真正做抽取合并的还是 pick。它体现了原文档推崇的函数组合思想——小函数各自干一件事再拼装成更复杂的行为。补充说明原始对象始终不变两个版本都不会修改传入的obj版本一里map每次生成新对象、reduce的初始值是{}版本二直接调用纯函数 pick。这一点与 jstips 中反复强调的不可变操作思路一致参见 2016-01-20-return-objects-to-enable-chaining-of-functions 中返回对象以支持链式调用的实践。如果你需要保留原对象不变例如 Redux 风格的 state 更新这种写法可以直接复用。拆解底层map、reduce 与 Object.assign 的协作理解 pick/reject 需要吃透三步操作各自扮演的角色这与 jstips 第 48 期 How to reduce() arrays 讲的内容一脉相承环节职责在本例中的作用Array.prototype.map一进一出、逐元素变换把键名数组变成单属性对象数组reject 中把自有键数组过滤后再变换Array.prototype.filter按条件保留元素reject 中用!keys.includes(k)剔除黑名单键Array.prototype.reduce把数组归约为单个值用Object.assign(res, o)把单属性对象逐次合并进累加器初始值为{}Object.assign把源对象属性拷贝到目标对象作为 reducer 的合并逻辑并承担浅拷贝职责需要留意Object.assign是浅拷贝如果挑选出的属性值本身是对象或数组得到的仍是对原值的引用。所以 pick/reject 适合处理扁平化的记录比如文档示例中表名.字段名扁平键若要深拷贝嵌套结构则需另行处理可参考 jstips 中关于不可变结构与克隆的讨论 2017-06-14-immutable-structures-and-cloning。现代写法对比解构、展开运算符与一行实现原文档写于 ES6 普及早期如今同样的需求可以用更现代的语法更紧凑地表达。基于解构与展开运算符的实现jstips 第 43 期 Use destructuring in function parameters 展示了解构赋值的威力结合第 78 期 What is a spread operator 中对象的展开合并可以写出更直观的版本// pick先解构出目标键再把其余部分展开成新对象 function pick(obj, keys) { return keys.reduce( (acc, k) (k in obj ? { ...acc, [k]: obj[k] } : acc), {} ); } // reject先删除黑名单键复制一份再 delete再展开返回 function reject(obj, keys) { const copy { ...obj }; keys.forEach(k delete copy[k]); return copy; } reject({a: 2, b: 3, c: 4}, [a, b]); // {c: 4}其中{ ...acc, [k]: obj[k] }直接利用对象展开与计算属性名完成拷贝 加键替代了Object.assignreject 则利用展开运算符先复制、再delete语义比 filter 版本更直白。现代 ES 的Object.fromEntries极简版如果你运行环境支持较新的 ECMAScript还可以用Object.fromEntries把 pick/reject 压缩成更短的管线const pick (obj, keys) Object.fromEntries(keys.filter(k k in obj).map(k [k, obj[k]])); const reject (obj, keys) Object.fromEntries(Object.entries(obj).filter(([k]) !keys.includes(k)));两种写法都保持了原文档的核心思想先选键、后取值、再组装成新对象只是组装手段从Object.assign换成了对象展开或Object.fromEntries。实战建议与注意事项键的判断统一用in既要挑出值为 falsy 的属性如0、、false也要覆盖原型链上继承的属性若只关心自有属性可改用Object.prototype.hasOwnProperty.call(obj, k)。输出始终是新对象pick/reject 都不应该修改入参这保证它们在table.map(...)、函数式流水线或状态更新场景中可以安全复用。浅拷贝边界Object.assign、展开运算符都只做一层拷贝嵌套对象仍共享引用扁平数据结构如数据库行下使用最顺手。性能与可读性权衡原版的 mapreduce 两步走对超大对象会有额外中间数组开销数据量可观时直接遍历键名并赋值到结果对象或上述 reduce 单遍版本更高效同时保持可读性。小结jstips 第 70 期用不到 70 行代码展示了 JavaScript 对象操作中挑选pick与剔除reject两个高频需求的纯函数解法pick 用mapObject.assignreduce实现白名单抽取reject 用Object.keysfilter反向生成白名单再复用 pick。这套思路不依赖任何第三方库可以直接应用到数据库行投影、API 响应裁剪、配置项过滤等真实场景结合仓库中关于in运算符第 10 期、reduce第 48 期、解构第 43 期和展开运算符第 78 期的系列内容你可以在 原文档 基础上继续深挖把选键-取值-重组的心智模型应用到更复杂的对象处理管线中。赞分享教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载相关推荐lodash对象属性筛选pick、omit与pickBy使用技巧lodash对象属性筛选pick、omit与pickBy使用技巧 在JavaScript开发中处理对象数据时经常需要筛选属性。Lodash提供了三个实用函数前端后端type-challenges 中等题解OmitByType —— 按属性类型而非键名精准剔除对象字段type challenges 中等题解OmitByType —— 按属性类型而非键名精准剔除对象字段 导读 OmitByType 是 type chal示例工程es-toolkit 的 pick 函数从对象中精准提取指定属性es toolkit 的 pick 函数从对象中精准提取指定属性 pick 是 es toolkit 对象工具集中最常用的函数之一它接收一个对象和一组键名前端后端上一篇如何5分钟快速安装Open Generative AI跨平台桌面应用完整指南下一篇ContextMenu未来路线图iOS 17适配与新功能展望创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表