一文解析JS如何准确获取对象自身的属性
作者:LuckySusu
在 JavaScript 开发中,我们经常需要遍历对象的属性。但你是否遇到过这样的问题:
“为什么遍历一个简单对象时,会多出一些意想不到的方法?”
这是因为 for...in 循环会遍历对象自身 + 原型链上所有可枚举的属性。如果我们只想获取对象“自己”的属性,就必须使用 hasOwnProperty() 方法进行过滤。
本文将深入讲解如何准确获取对象非原型链上的属性(即“自身属性”),并结合你提供的代码,给出最佳实践。
一、问题背景:for...in的“陷阱”
看一个经典例子:
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 实例自身的属性。
二、解决方案:使用hasOwnProperty()过滤
正确做法:使用 Object.prototype.hasOwnProperty() 方法判断属性是否属于对象自身。
function iterate(obj) {
const res = [];
for (let key in obj) {
// ✅ 只保留对象自身的属性
if (obj.hasOwnProperty(key)) {
res.push(key + ': ' + obj[key]);
}
}
return res;
}
const result = iterate(person);
console.log(result);
// 输出: ["name: Alice"]
// ✅ 成功过滤掉了原型上的 sayHello
三、hasOwnProperty原理详解
什么是“自身属性”(Own Property)?
- 自身属性:直接定义在对象实例上的属性,如
this.name = 'Alice'; - 继承属性:通过原型链从父级继承来的属性或方法,如
sayHello。
hasOwnProperty()的作用
- 检查某个属性是否是对象的直接属性;
- 返回
true表示该属性是自身的; - 返回
false表示该属性来自原型链或不存在。
person.hasOwnProperty('name'); // true ← 自身属性
person.hasOwnProperty('sayHello'); // false ← 来自原型
person.hasOwnProperty('toString'); // false ← 来自 Object.prototype
四、更现代的替代方案
虽然 hasOwnProperty 非常经典,但现代 JavaScript 提供了更多选择:
方法1:Object.keys()—— 获取所有自身可枚举属性
const ownKeys = Object.keys(person);
console.log(ownKeys); // ['name']
// 结合 map 处理
const result = Object.keys(person).map(key => {
return `${key}: ${person[key]}`;
});
优点:简洁,无需手动过滤;
缺点:只包含可枚举属性。
方法2:Object.getOwnPropertyNames()—— 包括不可枚举属性
// 添加一个不可枚举属性
Object.defineProperty(person, 'age', {
value: 25,
enumerable: false
});
console.log(Object.keys(person)); // ['name']
console.log(Object.getOwnPropertyNames(person)); // ['name', 'age']
适用场景:需要获取 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的潜在风险与规避
风险:对象可能重写了hasOwnProperty
const badObj = {
name: 'Test',
hasOwnProperty: function () {
return false; // 恶意重写
}
};
badObj.hasOwnProperty('name'); // false ❌ 错误结果!
安全调用方式
使用 call 或 Object.prototype.hasOwnProperty.call():
Object.prototype.hasOwnProperty.call(badObj, 'name'); // true ✅
// 或
{}.hasOwnProperty.call(badObj, 'name'); // true ✅
推荐在库或通用代码中使用这种写法,确保健壮性。
六、完整工具函数推荐
结合你提供的 iterate 函数,我们可以优化为更健壮的版本:
function getOwnProperties(obj) {
const res = [];
// 使用安全的 hasOwnProperty 调用
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
res.push(`${key}: ${obj[key]}`);
}
}
return res;
}
// 或者使用现代 API
function getOwnPropertiesModern(obj) {
return Object.keys(obj).map(key => `${key}: ${obj[key]}`);
}
七、总结:获取对象自身属性的方法对比
| 方法 | 是否包含原型 | 是否包含不可枚举 | 是否包含 Symbol | 推荐场景 |
|---|---|---|---|---|
| for...in + hasOwnProperty | ❌ 否 | ❌ 否 | ❌ 否 | 兼容旧环境 |
| Object.keys() | ❌ 否 | ❌ 否 | ❌ 否 | 日常开发,简洁 |
| Object.getOwnPropertyNames() | ❌ 否 | ✅ 是 | ❌ 否 | 需要不可枚举属性 |
| Reflect.ownKeys() | ❌ 否 | ✅ 是 | ✅ 是 | 全面获取所有键 |
结语
“遍历对象时,for...in 是‘广撒网’,hasOwnProperty 是‘精准捕捞’。”
掌握如何区分自身属性与继承属性,是写出高质量 JavaScript 代码的基本功。无论你是做数据处理、对象克隆,还是开发类库,这个知识点都至关重要。
记住:
- 用
hasOwnProperty过滤原型属性; - 优先使用
Object.keys()等现代 API; - 在通用代码中使用
Object.prototype.hasOwnProperty.call()保证安全。
到此这篇关于一文解析JS如何准确获取对象自身的属性的文章就介绍到这了,更多相关JS获取对象自身属性内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
