一文解析JS如何準(zhǔn)確獲取對(duì)象自身的屬性
在 JavaScript 開發(fā)中,我們經(jīng)常需要遍歷對(duì)象的屬性。但你是否遇到過這樣的問題:
“為什么遍歷一個(gè)簡(jiǎn)單對(duì)象時(shí),會(huì)多出一些意想不到的方法?”
這是因?yàn)?for...in 循環(huán)會(huì)遍歷對(duì)象自身 + 原型鏈上所有可枚舉的屬性。如果我們只想獲取對(duì)象“自己”的屬性,就必須使用 hasOwnProperty() 方法進(jìn)行過濾。
本文將深入講解如何準(zhǔn)確獲取對(duì)象非原型鏈上的屬性(即“自身屬性”),并結(jié)合你提供的代碼,給出最佳實(shí)踐。
一、問題背景:for...in的“陷阱”
看一個(gè)經(jīng)典例子:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log(`Hello, I'm ${this.name}`);
};
const person = new Person('Alice');
// 直接使用 for...in
for (let key in person) {
console.log(key);
}
// 輸出:
// name
// sayHello ← 這是原型上的方法,但我們可能不想要它!
問題:sayHello 是從原型鏈繼承來的,并非 person 實(shí)例自身的屬性。
二、解決方案:使用hasOwnProperty()過濾
正確做法:使用 Object.prototype.hasOwnProperty() 方法判斷屬性是否屬于對(duì)象自身。
function iterate(obj) {
const res = [];
for (let key in obj) {
// ? 只保留對(duì)象自身的屬性
if (obj.hasOwnProperty(key)) {
res.push(key + ': ' + obj[key]);
}
}
return res;
}
const result = iterate(person);
console.log(result);
// 輸出: ["name: Alice"]
// ? 成功過濾掉了原型上的 sayHello
三、hasOwnProperty原理詳解
什么是“自身屬性”(Own Property)?
- 自身屬性:直接定義在對(duì)象實(shí)例上的屬性,如
this.name = 'Alice'; - 繼承屬性:通過原型鏈從父級(jí)繼承來的屬性或方法,如
sayHello。
hasOwnProperty()的作用
- 檢查某個(gè)屬性是否是對(duì)象的直接屬性;
- 返回
true表示該屬性是自身的; - 返回
false表示該屬性來自原型鏈或不存在。
person.hasOwnProperty('name'); // true ← 自身屬性
person.hasOwnProperty('sayHello'); // false ← 來自原型
person.hasOwnProperty('toString'); // false ← 來自 Object.prototype
四、更現(xiàn)代的替代方案
雖然 hasOwnProperty 非常經(jīng)典,但現(xiàn)代 JavaScript 提供了更多選擇:
方法1:Object.keys()—— 獲取所有自身可枚舉屬性
const ownKeys = Object.keys(person);
console.log(ownKeys); // ['name']
// 結(jié)合 map 處理
const result = Object.keys(person).map(key => {
return `${key}: ${person[key]}`;
});
優(yōu)點(diǎn):簡(jiǎn)潔,無(wú)需手動(dòng)過濾;
缺點(diǎn):只包含可枚舉屬性。
方法2:Object.getOwnPropertyNames()—— 包括不可枚舉屬性
// 添加一個(gè)不可枚舉屬性
Object.defineProperty(person, 'age', {
value: 25,
enumerable: false
});
console.log(Object.keys(person)); // ['name']
console.log(Object.getOwnPropertyNames(person)); // ['name', 'age']
適用場(chǎng)景:需要獲取 configurable: false 或 enumerable: false 的屬性。
方法3:Reflect.ownKeys()—— 最全的自身屬性列表
const obj = { a: 1 };
Object.defineProperty(obj, 'b', { value: 2, enumerable: false });
obj[Symbol('c')] = 3;
console.log(Reflect.ownKeys(obj)); // ['a', 'b', Symbol(c)]
包含:字符串鍵、Symbol 鍵、可枚舉和不可枚舉屬性。
五、hasOwnProperty的潛在風(fēng)險(xiǎn)與規(guī)避
風(fēng)險(xiǎn):對(duì)象可能重寫了hasOwnProperty
const badObj = {
name: 'Test',
hasOwnProperty: function () {
return false; // 惡意重寫
}
};
badObj.hasOwnProperty('name'); // false ? 錯(cuò)誤結(jié)果!
安全調(diào)用方式
使用 call 或 Object.prototype.hasOwnProperty.call():
Object.prototype.hasOwnProperty.call(badObj, 'name'); // true ?
// 或
{}.hasOwnProperty.call(badObj, 'name'); // true ?
推薦在庫(kù)或通用代碼中使用這種寫法,確保健壯性。
六、完整工具函數(shù)推薦
結(jié)合你提供的 iterate 函數(shù),我們可以優(yōu)化為更健壯的版本:
function getOwnProperties(obj) {
const res = [];
// 使用安全的 hasOwnProperty 調(diào)用
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
res.push(`${key}: ${obj[key]}`);
}
}
return res;
}
// 或者使用現(xiàn)代 API
function getOwnPropertiesModern(obj) {
return Object.keys(obj).map(key => `${key}: ${obj[key]}`);
}
七、總結(jié):獲取對(duì)象自身屬性的方法對(duì)比
| 方法 | 是否包含原型 | 是否包含不可枚舉 | 是否包含 Symbol | 推薦場(chǎng)景 |
|---|---|---|---|---|
| for...in + hasOwnProperty | ? 否 | ? 否 | ? 否 | 兼容舊環(huán)境 |
| Object.keys() | ? 否 | ? 否 | ? 否 | 日常開發(fā),簡(jiǎn)潔 |
| Object.getOwnPropertyNames() | ? 否 | ? 是 | ? 否 | 需要不可枚舉屬性 |
| Reflect.ownKeys() | ? 否 | ? 是 | ? 是 | 全面獲取所有鍵 |
結(jié)語(yǔ)
“遍歷對(duì)象時(shí),for...in 是‘廣撒網(wǎng)’,hasOwnProperty 是‘精準(zhǔn)捕撈’。”
掌握如何區(qū)分自身屬性與繼承屬性,是寫出高質(zhì)量 JavaScript 代碼的基本功。無(wú)論你是做數(shù)據(jù)處理、對(duì)象克隆,還是開發(fā)類庫(kù),這個(gè)知識(shí)點(diǎn)都至關(guān)重要。
記住:
- 用
hasOwnProperty過濾原型屬性; - 優(yōu)先使用
Object.keys()等現(xiàn)代 API; - 在通用代碼中使用
Object.prototype.hasOwnProperty.call()保證安全。
到此這篇關(guān)于一文解析JS如何準(zhǔn)確獲取對(duì)象自身的屬性的文章就介紹到這了,更多相關(guān)JS獲取對(duì)象自身屬性內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
20分鐘輕松創(chuàng)建自己的Bootstrap站點(diǎn)
這篇文章主要教大家如何在短短的20分鐘內(nèi)輕松創(chuàng)建自己的Bootstrap站點(diǎn),學(xué)會(huì)使用twitter bootstrap建立一個(gè)站點(diǎn),從而鞏固Bootstrap一系列基礎(chǔ)知識(shí),感興趣的小伙伴們可以參考一下2016-05-05
在JavaScript中如何將數(shù)字格式化為指定位數(shù)
本文介紹了在JavaScript中將數(shù)字格式化為3位,不足3位時(shí)在前面補(bǔ)0的幾種方法,每種方法結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-11-11
js實(shí)現(xiàn)人民幣大寫金額形式轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)人民幣大寫金額形式轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下2016-04-04
JavaScript統(tǒng)計(jì)字符串中每個(gè)字符出現(xiàn)次數(shù)完整實(shí)例
這篇文章主要介紹了JavaScript統(tǒng)計(jì)字符串中每個(gè)字符出現(xiàn)次數(shù)的方法,以完整實(shí)例形式分析了JavaScript針對(duì)字符串中字符的遍歷操作相關(guān)技巧,需要的朋友可以參考下2016-01-01
微信JS-SDK坐標(biāo)位置如何轉(zhuǎn)換為百度地圖坐標(biāo)
這篇文章主要介紹了微信JS-SDK坐標(biāo)位置如何轉(zhuǎn)換為百度地圖坐標(biāo) 的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-07-07

