JavaScript获取对象属性的方法对比
方法对比表
| 方法 | 是否包含非迭代属性 | 是否包含原型链属性 | 说明 |
|---|---|---|---|
Object.keys(obj) | ❌ | ❌ | 只返回对象自身的可枚举属性名 |
Object.getOwnPropertyNames(obj) | ✅ | ❌ | 返回对象自身的所有属性名(包括不可枚举) |
for...in 循环 | ❌ | ✅ | 遍历对象及其原型链上的可枚举属性 |
JSON.stringify(obj) | ❌ | ❌ | 只序列化对象自身的可枚举属性 |
详细说明
1. Object.keys()
const obj = {
a: 1,
b: 2
};
Object.defineProperty(obj, 'c', {
value: 3,
enumerable: false // 不可枚举
});
console.log(Object.keys(obj)); // ['a', 'b']特点:
- 只返回对象自身的可枚举属性
- 不包含不可枚举属性
- 不包含原型链上的属性
- 返回数组格式
2. Object.getOwnPropertyNames()
const obj = {
a: 1,
b: 2
};
Object.defineProperty(obj, 'c', {
value: 3,
enumerable: false // 不可枚举
});
console.log(Object.getOwnPropertyNames(obj)); // ['a', 'b', 'c']特点:
- 返回对象自身的所有属性(包括不可枚举)
- 不包含原型链上的属性
- 返回数组格式
- 适用于需要完整属性列表的场景
3. for…in 循环
function Parent() {
this.parentProp = 'parent';
}
Parent.prototype.prototypeProp = 'prototype';
function Child() {
this.childProp = 'child';
}
Child.prototype = new Parent();
const obj = new Child();
Object.defineProperty(obj, 'nonEnumerable', {
value: 'hidden',
enumerable: false
});
for (let key in obj) {
console.log(key); // 输出: childProp, parentProp, prototypeProp
}特点:
- 遍历对象及原型链上的可枚举属性
- 不包含不可枚举属性
- 包含继承的属性
- 需要配合
obj.hasOwnProperty(key)过滤自身属性
4. JSON.stringify()
const obj = {
a: 1,
b: 2
};
Object.defineProperty(obj, 'c', {
value: 3,
enumerable: false
});
console.log(JSON.stringify(obj)); // '{"a":1,"b":2}'特点:
- 只序列化可枚举的自身属性
- 忽略不可枚举属性
- 忽略原型链属性
- 返回JSON字符串格式
设置非迭代属性示例
使用 Object.defineProperty()
const obj = {
a: 1, // 可枚举属性
};
// 添加一个非迭代(不可枚举)属性
Object.defineProperty(obj, 'b', {
value: 2,
enumerable: false, // 设为非迭代属性
writable: true, // 可写
configurable: true // 可配置
});
console.log(Object.keys(obj)); // 输出: ['a']
console.log(Object.getOwnPropertyNames(obj)); // 输出: ['a', 'b']
console.log(obj.b); // 输出: 2 (属性存在,但不可枚举)批量设置非迭代属性
const obj = { a: 1 };
Object.defineProperties(obj, {
b: {
value: 2,
enumerable: false
},
c: {
value: 3,
enumerable: false
}
});
console.log(Object.keys(obj)); // ['a']
console.log(Object.getOwnPropertyNames(obj)); // ['a', 'b', 'c']实际应用场景
1. 库开发中的内部属性
class MyClass {
constructor() {
this.publicProp = 'public';
// 设置内部属性为不可枚举
Object.defineProperty(this, '_internal', {
value: 'internal',
enumerable: false,
writable: true
});
}
}
const instance = new MyClass();
console.log(Object.keys(instance)); // ['publicProp']
// 内部属性不会在普通遍历中出现2. 配置对象的元数据
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
// 添加元数据,但不希望在序列化时包含
Object.defineProperty(config, '_version', {
value: '1.0.0',
enumerable: false
});
console.log(JSON.stringify(config));
// '{"apiUrl":"https://api.example.com","timeout":5000}'
// _version 不会被序列化3. 检查对象的完整属性
function getAllProperties(obj) {
const ownEnumerable = Object.keys(obj);
const ownNonEnumerable = Object.getOwnPropertyNames(obj)
.filter(key => !obj.propertyIsEnumerable(key));
const inherited = [];
for (let key in obj) {
if (!obj.hasOwnProperty(key)) {
inherited.push(key);
}
}
return {
ownEnumerable,
ownNonEnumerable,
inherited
};
}相关概念
属性描述符
const descriptor = Object.getOwnPropertyDescriptor(obj, 'propertyName');
console.log(descriptor);
// {
// value: ...,
// writable: true/false,
// enumerable: true/false,
// configurable: true/false
// }属性枚举性检查
obj.propertyIsEnumerable('propertyName'); // true/false
Object.prototype.propertyIsEnumerable.call(obj, 'propertyName');最佳实践
- 遍历自身属性: 使用
Object.keys()或Object.getOwnPropertyNames() - 检查属性存在: 使用
obj.hasOwnProperty(key)而非key in obj - 库开发: 将内部属性设为不可枚举,避免影响用户代码
- 序列化控制: 利用不可枚举属性控制JSON序列化输出
- 性能考虑:
Object.keys()通常比for...in+hasOwnProperty更快