jstips 第 10 期:检查对象属性是否存在的正确姿势 —— `in` 操作符与 `hasOwnProperty` 深度对比
教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载导读在 JavaScript 日常开发中判断一个对象是否拥有某个属性是最高频的操作之一。本文以 jstips 仓库 Check if a property is in a Object第 10 期技巧为骨架系统讲解if (obj.prop)直接判断的陷阱、in操作符与Object.prototype.hasOwnProperty两种原生方案的本质差异关键在“检查深度”自身属性 vs 原型链继承属性并结合现代 ES2022 的Object.hasOwn给出兼顾安全与兼容的实战建议。读完你将能准确选择属性存在性检查方案避免原型链污染与隐式类型转换带来的隐蔽 Bug。一、最常见的做法if (myObject.name)为什么不够严谨原文档首先指出很多开发者判断属性是否存在时习惯这样写var myObject { name: tips_js }; if (myObject.name) { ... }这段代码在name属性存在且值为真值时能正常工作但它存在两个致命缺陷真值性truthy检查不等于存在性检查。if (myObject.name)实际做的是“myObject.name是否为真值”而不是“myObject上是否定义了name”。当属性的值恰好是、0、false、null、undefined或NaN这类假值falsy时即使属性真实存在条件也会判定为不成立。例如var o { count: 0 }; if (o.count) { ... }永远不会进入分支但count属性确实存在。无法区分“属性不存在”与“属性存在但值为假”。很多业务 Bug 正源于此默认值覆盖、条件渲染、配置项合并等场景中开发者需要精确区分这两种情况。因此原文档推荐使用两种原生方法in操作符与Object.hasOwnProperty。它们都来自Object原型链任何继承自Object的对象都可以直接调用。二、两种原生方案in操作符in操作符用于判断某个属性是否存在于对象自身或其原型链上语法为prop in object其中prop是字符串或 Symbol 类型的属性名object是需要检查的对象。如果属性存在于对象自身或沿原型链向上可达返回true否则返回false。var myObject { name: tips_js }; name in myObject; // true age in myObject; // false三、两种原生方案Object.prototype.hasOwnPropertyhasOwnProperty是Object.prototype上的方法它只检查属性是否为对象自身的直接属性own property完全不考虑原型链myObject.hasOwnProperty(name); // true注意它是原型链上的方法因此几乎所有对象除Object.create(null)创建的空原型对象外都能直接调用。如果对象是通过Object.create(null)创建的它没有任何原型方法直接调用obj.hasOwnProperty(...)会报错此时应改用Object.prototype.hasOwnProperty.call(obj, key)或 ES2022 的Object.hasOwn。四、核心差异检查深度不同原文档核心示例原文档用一个对比示例展示了两种方案的“深度”差异var myObject { name: tips_js }; myObject.hasOwnProperty(name); // true name in myObject; // true myObject.hasOwnProperty(valueOf); // false, valueOf 继承自原型链 valueOf in myObject; // true结论一目了然hasOwnProperty只在该属性是对象自身直接属性时返回truein操作符不区分属性是对象自身创建的还是从原型链继承来的只要链上能找到就返回true。valueOf、toString、constructor、hasOwnProperty本身等都属于Object.prototype上的继承属性所以valueOf in myObject为true而myObject.hasOwnProperty(valueOf)为false。构造函数的原型链示例原文档进一步用构造函数验证这一差异var myFunc function() { this.name tips_js; }; myFunc.prototype.age 10 days; var user new myFunc(); user.hasOwnProperty(name); // true user.hasOwnProperty(age); // false, 因为 age 来自原型链这里name是构造函数体内通过this.name ...创建的实例自有属性而age挂在myFunc.prototype上因此name in user; // true自身属性 age in user; // true原型链上可达 user.hasOwnProperty(age); // false不是自身属性这正是“检查深度不同”的完整含义in覆盖整条原型链hasOwnProperty只覆盖对象自身。五、实战误区与原型链“假阳性”理解上述差异后几个经典误区需要特别警惕for...in遍历与hasOwnProperty的经典搭配。for...in会遍历包括继承属性在内的所有可枚举属性因此老代码中常见if (obj.hasOwnProperty(key))过滤。仓库另一篇技巧 Hash maps without side effects 正是针对此问题使用Object.create(null)创建无原型对象遍历时便无需再做自身属性检查也彻底避免in/for...in被原型链污染的问题。属性名恰好覆盖原型方法。若对象自身定义了名为hasOwnProperty的属性例如数据中恰好有该 key直接调用obj.hasOwnProperty(key)会得到非函数而抛错这也是原生方法被“屏蔽”的经典坑。用in判断“安全存在”并不安全。in只回答“链上是否存在”不回答“是否属于你”在序列化、深拷贝、白名单过滤等场景若混入继承属性会导致数据污染。仓库 Picking and rejecting object properties 中的pick实现使用k in obj判断后取值正是因为pick业务上希望容忍原型链属性而reject实现改用Object.keys(obj)仅枚举自身可枚举属性两种工具对“深度”的不同取舍恰与本文主题互为印证。六、现代演进Object.hasOwn与更多替代方案针对hasOwnProperty被覆盖、以及Object.create(null)对象无该方法的问题ES2022 引入了静态方法Object.hasOwn(obj, prop)Object.hasOwn(myObject, name); // true Object.hasOwn(myObject, valueOf); // false它等价于Object.prototype.hasOwnProperty.call(obj, prop)且对Object.create(null)对象同样安全。此外还有几种常用方案Object.keys(obj)返回自身可枚举属性名数组可用includes判断缺点是不可枚举属性与 Symbol 属性不会出现。Object.getOwnPropertyNames(obj)返回自身所有字符串属性名含不可枚举不含 Symbol。Reflect.ownKeys(obj)返回自身所有属性键含不可枚举与 Symbol。选择建议需求推荐方案只关心对象自身属性含不可枚举ES2022 环境Object.hasOwn(obj, key)需要兼容 ES5/旧环境Object.prototype.hasOwnProperty.call(obj, key)需要沿原型链判断属性可达性key in obj需要“值存在且非假值”的业务判断if (obj[key])明确知道语义前提需要遍历自身可枚举属性Object.keys(obj)七、小结按语义选择检查方式回到原文档的核心结论判断属性是否存在不应依赖真值判断而应在“自身属性”与“原型链可达属性”两个语义之间做出明确选择——前者用hasOwnProperty/Object.hasOwn后者用in。原文档最后还建议读者参考社区关于“检查属性存在时的常见错误”的讨论见仓库对应 issue 记录。在 jstips 仓库中本文对应的中文翻译见 _posts/zh_CN/javascript/2016-01-10-check-if-a-property-is-in-a-object.md繁体版见 _posts/zh_TW/javascript/2016-01-10-check-if-a-property-is-in-a-object.md西班牙语版见 _posts/es_ES/javascript/2016-01-10-check-if-a-property-is-in-a-object.md可供对照阅读。相关延伸还可参考仓库 Advanced Properties属性描述符与 getter/setter、Why you should use Object.is() in equality comparison-in-equality-comparison.md)相等性比较与 Short circuit evaluation in JS短路求值它们共同构成 JavaScript 对象操作中“精确判断”的完整工具箱。赞分享教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载相关推荐Vuetify 无障碍a11y实践指南Activator 语义属性与键盘焦点管理Vuetify 无障碍a11y实践指南Activator 语义属性与键盘焦点管理 Web 无障碍Accessibility简称 a11y是一项包容性教程freeCodeCamp Basic JavaScript 挑战精讲用 hasOwnProperty() 检测对象的属性是否存在freeCodeCamp Basic JavaScript 挑战精讲用 hasOwnProperty 检测对象的属性是否存在 本文基于 freeCodeCam前端后端教育千古前端图文教程JavaScript 对象的高级操作——hasOwnProperty 属性检测与 Object.freeze 对象冻结千古前端图文教程JavaScript 对象的高级操作——hasOwnProperty 属性检测与 Object.freeze 对象冻结 本文是《04 Java文档教程前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考