【js篇】如何准确获取对象自身的属性?hasOwnProperty深度解析

在 JavaScript 开发中,我们经常需要遍历对象的属性。但你是否遇到过这样的问题:

"为什么遍历一个简单对象时,会多出一些意想不到的方法?"

这是因为 for...in 循环会遍历对象自身 + 原型链上所有可枚举的属性 。如果我们只想获取对象"自己"的属性,就必须使用 hasOwnProperty() 方法进行过滤。

本文将深入讲解如何准确获取对象非原型链上的属性(即"自身属性"),并结合你提供的代码,给出最佳实践。


一、问题背景:for...in 的"陷阱"

看一个经典例子:

js 复制代码
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() 方法判断属性是否属于对象自身。

js 复制代码
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 表示该属性来自原型链或不存在。
js 复制代码
person.hasOwnProperty('name');     // true  ← 自身属性
person.hasOwnProperty('sayHello'); // false ← 来自原型
person.hasOwnProperty('toString'); // false ← 来自 Object.prototype

四、更现代的替代方案

虽然 hasOwnProperty 非常经典,但现代 JavaScript 提供了更多选择:

✅ 方法1:Object.keys() ------ 获取所有自身可枚举属性

js 复制代码
const ownKeys = Object.keys(person);
console.log(ownKeys); // ['name']

// 结合 map 处理
const result = Object.keys(person).map(key => {
  return `${key}: ${person[key]}`;
});

✅ 优点:简洁,无需手动过滤; ❌ 缺点:只包含可枚举属性。


✅ 方法2:Object.getOwnPropertyNames() ------ 包括不可枚举属性

js 复制代码
// 添加一个不可枚举属性
Object.defineProperty(person, 'age', {
  value: 25,
  enumerable: false
});

console.log(Object.keys(person));           // ['name']
console.log(Object.getOwnPropertyNames(person)); // ['name', 'age']

✅ 适用场景:需要获取 configurable: falseenumerable: false 的属性。


✅ 方法3:Reflect.ownKeys() ------ 最全的自身属性列表

js 复制代码
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

js 复制代码
const badObj = {
  name: 'Test',
  hasOwnProperty: function () {
    return false; // 恶意重写
  }
};

badObj.hasOwnProperty('name'); // false ❌ 错误结果!

✅ 安全调用方式

使用 callObject.prototype.hasOwnProperty.call()

js 复制代码
Object.prototype.hasOwnProperty.call(badObj, 'name'); // true ✅
// 或
{}.hasOwnProperty.call(badObj, 'name'); // true ✅

📌 推荐在库或通用代码中使用这种写法,确保健壮性。


六、完整工具函数推荐

结合你提供的 iterate 函数,我们可以优化为更健壮的版本:

js 复制代码
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() 保证安全。
相关推荐
LuckySusu1 小时前
【js篇】深入理解 JavaScript 作用域与作用域链
前端·javascript
LuckySusu1 小时前
【js篇】call() 与 apply()深度对比
前端·javascript
LuckySusu1 小时前
【js篇】addEventListener()方法的参数和使用
前端·javascript
该用户已不存在1 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net
LuckySusu2 小时前
【js篇】深入理解 JavaScript 原型与原型链
前端·javascript
文心快码BaiduComate2 小时前
文心快码入选2025服贸会“数智影响力”先锋案例
前端·后端·程序员
云枫晖2 小时前
手写Promise-构造函数
前端·javascript
文心快码BaiduComate2 小时前
用Comate Zulu开发一款微信小程序
前端·后端·微信小程序
王王碎冰冰2 小时前
基于 Vue3@3.5+跟Ant Design of Vue 的二次封装的 Form跟搜索Table
前端·vue.js