news 2026/10/23 21:50:41

jstips 系列第 10 期:徹底掌握 JavaScript 物件屬性檢查——`in` 運算子與 `hasOwnProperty` 的深度差異

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
jstips 系列第 10 期:徹底掌握 JavaScript 物件屬性檢查——`in` 運算子與 `hasOwnProperty` 的深度差異
  • 教程

【免费下载链接】jstips

This is about useful JS tips!

项目地址:https://gitcode.com/gh_mirrors/js/jstips
点击查看免费下载

本文對應 jstips 倉庫第 10 期技巧(繁體中文版:_posts/zh_TW/javascript/2016-01-10-check-if-a-property-is-in-a-object.md,另有英文原版、簡體中文版與西班牙語版)。本篇將從最常見的直覺寫法出發,完整對比 JavaScript 中檢查物件屬性是否存在的兩大原生手段——in運算子與Object.prototype.hasOwnProperty,剖析兩者在「自身屬性」與「原型鏈繼承屬性」上的核心差異,並結合本倉庫其他技巧(如無原型物件、屬性挑選等)說明何時該用哪一種,幫你寫出正確、高效且無隱患的屬性存在性判斷。

一、最常見的寫法:if (myObject.name)及其陷阱

檢查某個屬性是否存在於物件中,許多開發者的第一反應是直接讀取值並判斷真偽:

var myObject = { name: '@tips_js' }; if (myObject.name) { ... }

這段寫法「能用」,但它其實隱藏著一個關鍵缺陷:它判斷的是屬性的「真值(truthy)」狀態,而不是屬性的「存在與否」。JavaScript 中所有 falsy 值——0、''(空字串)、false、null、undefined、NaN——都會讓這個條件判斷失敗,即使屬性本身確實存在:

var config = { retries: 0, // 屬性存在,但值為 falsy label: '', // 屬性存在,但值為 falsy enabled: false // 屬性存在,但值為 falsy }; if (config.retries) { /* 不會執行,即使 retries 已定義且等於 0 */ } if (config.label) { /* 不會執行,即使 label 已定義為空字串 */ }

換言之,只要屬性的值恰好落在 falsy 集合裡,if (obj.prop)就會誤判為「屬性不存在」。若要精確回答「這個 key 到底存不存在」,JavaScript 提供了兩個原生方法:in運算子與Object.hasOwnProperty。任何繼承自Object的物件,都可以使用這兩種方式——這是本技巧(tip 10)的核心內容,也與倉庫中第 5 期「undefined與null的差異」所討論的 falsy/truthy 語義一脈相承。

二、兩種原生方法:in運算子與hasOwnProperty

2.1in運算子

in是 JavaScript 的內建運算子,語法為propertyName in object。它會沿著物件的整個原型鏈向上查找,只要在自身屬性或任何一層繼承屬性中找到該 key,就回傳true:

var myObject = { name: '@tips_js' }; 'name' in myObject; // true,自身屬性 'valueOf' in myObject; // true,繼承自原型鏈

注意in運算子的左側必須是字串形式的屬性名稱(或會被轉換為字串的表達式),且當右側不是物件時會拋出TypeError。

2.2Object.prototype.hasOwnProperty

hasOwnProperty是定義在Object.prototype上的方法,任何普通物件都能直接呼叫。它只檢查自身屬性,不往上追溯原型鏈:

var myObject = { name: '@tips_js' }; myObject.hasOwnProperty('name'); // true myObject.hasOwnProperty('valueOf'); // false,valueOf 繼承自原型鏈

2.3 兩者的本質差異

原文用一段對比清晰地呈現了兩者的差別:

var myObject = { name: '@tips_js' }; myObject.hasOwnProperty('name'); // true 'name' in myObject; // true myObject.hasOwnProperty('valueOf'); // false, valueOf 繼承自原型鏈結 'valueOf' in myObject; // true

兩者不同的地方在於確認屬性的深度不同。換句話說,如果 key 直接存在於物件本身,hasOwnProperty才會回傳true;而in運算子無法分辨屬性是建立在物件上,還是繼承自原型鏈——只要是沿著原型鏈可達的屬性,in一律回答「存在」。

這個差異在實務上非常關鍵:任何普通物件都繼承了Object.prototype上的方法,例如constructor、toString、valueOf、hasOwnProperty、isPrototypeOf等。因此:

  • 'toString' in {}→true(繼承而來)
  • {}.hasOwnProperty('toString')→false(並非自身屬性)

三、原型鏈情境下的對比:構造函數範例

原文給出了第二組更貼近真實開發的範例,透過構造函數與prototype展示繼承屬性的行為:

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 繼承自原型鏈結

這段程式碼說明了三件事:

  1. 在構造函數內用this.name = ...賦值的屬性,會成為每個實例的自身屬性,因此user.hasOwnProperty('name')為true;
  2. 掛在myFunc.prototype上的age,會被所有實例透過原型鏈「看到」,但它不是實例的自身屬性,所以user.hasOwnProperty('age')為false;
  3. 若改用in運算子,'age' in user則會回傳true——因為in會沿著user → myFunc.prototype → Object.prototype這條鏈逐一查找。

由此可以歸納出選擇原則:

判斷目標推薦方式行為
只關心「自身屬性」是否存在(例如資料校驗、白名單過濾)Object.prototype.hasOwnProperty不追溯原型鏈
關心「可讀取到該屬性」(含繼承屬性,例如特性偵測)in運算子沿原型鏈完整查找

四、現代進化:Object.hasOwn與無原型物件

4.1Object.hasOwn(ES2022)

hasOwnProperty有一個已知弱點:如果物件本身被賦予了名為hasOwnProperty的屬性(例如var o = { hasOwnProperty: null }),直接呼叫o.hasOwnProperty(...)就會失敗或行為異常。ES2022 提供了更穩健的靜態方法Object.hasOwn(obj, prop),它不依賴物件自身的同名屬性,是取代hasOwnProperty的推薦寫法:

Object.hasOwn({ name: '@tips_js' }, 'name'); // true Object.hasOwn({ name: '@tips_js' }, 'valueOf'); // false

4.2Object.create(null):完全繞過原型鏈

當你需要一個「純資料容器」、不想被任何繼承屬性干擾時,可以建立無原型物件。本倉庫第 73 期「無副作用雜湊表(Hash maps without side effects)」正是這個主題的代表:

透過Object.create(null),我們明確指定null作為其原型。因此它完全沒有任何屬性——連constructor、toString、hasOwnProperty等都沒有——你可以放心地在資料結構中使用這些 key 當作鍵名。

該文同時展示了在遍歷雜湊表時使用hasOwnProperty來排除繼承屬性的典型寫法:

if (dirtyMap.hasOwnProperty(key)) { // 檢查以避開繼承屬性 // 僅處理自身屬性 }

這與本期技巧正好互為表裡:當你面對普通物件(帶有Object.prototype繼承鏈)時,必須用hasOwnProperty/Object.hasOwn或in來明確控制檢查深度;而Object.create(null)則從根源上消除繼承屬性的干擾。

五、倉庫中的實際應用佐證

jstips 倉庫內多篇技巧都在真實使用這兩種檢查方式,可作為最佳實踐的參考:

  1. 第 70 期「挑選與排除物件屬性」:其pick函式用k in obj判斷 key 是否存在於目標物件,再決定是否取出該屬性:
function pick(obj, keys) { return keys.map(k => k in obj ? {[k]: obj[k]} : {}) .reduce((res, o) => Object.assign(res, o), {}); }

這裡使用in而非hasOwnProperty,是因為pick的語義是「取出該物件(含繼承鏈)可讀到的屬性」,與in的深度行為一致。

  1. 第 73 期「無副作用雜湊表」:如上所述,在for...in這類會遍歷繼承屬性的語法中,用hasOwnProperty過濾掉非自身屬性,是經典的安全寫法。

  2. 第 39 期「進階屬性(Advanced Properties)」:講解Object.defineProperty定義value、writable、enumerable、configurable等屬性描述元。當屬性被設為enumerable: false時,for...in不會列出它,但in與hasOwnProperty仍會正確回報其存在——這提醒我們:屬性是否「可列舉」與屬性是否「存在」是兩個獨立維度。

六、實務選擇建議與常見錯誤總結

綜合原文與倉庫內相關技巧,給出以下決策指引:

  1. 判斷「是否存在」而非「是否 truthy」:當屬性值可能為0、''、false、null、undefined、NaN時,if (obj.prop)必然誤判。此時改用in或hasOwnProperty。
  2. 只要自身屬性(如表單欄位校驗、資料欄位白名單、for...in過濾):優先使用Object.hasOwn(obj, prop)(現代環境)或obj.hasOwnProperty(prop)(相容舊環境)。
  3. 需要沿原型鏈查找(如特性偵測、判斷某方法是否「可被呼叫」):使用in運算子。
  4. 避免呼叫被覆寫的hasOwnProperty:若物件可能帶有同名自訂屬性,改用Object.hasOwn或Object.prototype.hasOwnProperty.call(obj, key)。
  5. 純資料容器:需要徹底不受繼承屬性干擾時,使用Object.create(null)建立無原型物件(參見第 73 期)。

常見錯誤的根源在於:把「屬性值為 falsy」誤當成「屬性不存在」,以及把「屬性可沿原型鏈讀取」誤當成「屬性為自身所有」。釐清in與hasOwnProperty的深度差異後,這兩類問題都能迎刃而解——這正是本期 jstips 技巧(tip 10)要傳達的核心價值。

七、延伸閱讀

  • 第 5 期「undefined與null的差異」:falsy 值語義與typeof檢查的完整說明
  • 第 73 期「無副作用雜湊表」:Object.create(null)與hasOwnProperty的實戰組合
  • 第 70 期「挑選與排除物件屬性」:in運算子在屬性挑選工具中的應用
  • 第 39 期「進階屬性」:屬性描述元與可列舉性的深入講解
  • POST_TEMPLATE.md:jstips 文章格式範本,可了解本期技巧的標準結構
  • 教程

【免费下载链接】jstips

This is about useful JS tips!

项目地址:https://gitcode.com/gh_mirrors/js/jstips
点击查看免费下载
上一篇:Apache JMeter 完全技术指南:性能测试、安装运行与源码构建实战
下一篇:WebMCP 实战案例拆解:电商导购、创意设计与代码评审中 AI Agent 的 3 大应用场景

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 1:45:00

使用 PHP 绑定调用 Xberg::listRenderers() 管理文档渲染器注册表

后端AI 应用NLP 【免费下载链接】xberg Polyglot document intelligence with a Rust core: extract text, metadata, images, tables, and structured data from 106 formats across 140 file extensions, plus code intelligence for 371 languages. Fifteen bindings, with …

作者头像 李华
网站建设 2026/10/16 0:29:45

【ArkUI 练中学】第7课:动画体系

本节目标理解 ArkUI 动画体系的基本分类:属性动画、显式动画、转场动画、路径动画掌握 animateTo 显式动画接口的用法,理解其闭包驱动机制与适用场景掌握 .animation() 属性动画接口的用法,理解其声明式自动触发机制区分 animateTo 与…

作者头像 李华