遍历原型链可枚举属性应分场景:仅继承属性用for...in跳过hasOwnProperty;含自身的各层自有属性则循环Object.getPrototypeOf配合Object.keys;二者均忽略不可枚举属性和Symbol。

要遍历 JavaScript 原型链上所有祖先节点(包括自身)的**可枚举属性**,核心是利用 Object.getPrototypeOf() 逐级向上获取原型对象,并结合 Object.keys() 或 for...in 收集属性。但需注意:只遍历**可枚举属性**、避免重复、区分自有属性与继承属性。
只遍历可枚举的继承属性(推荐用 for...in)
for...in 会自动遍历对象自身及其整个原型链上所有**可枚举属性**(不包括 Symbol 属性和不可枚举属性),且天然去重(同名属性只出现最后一次定义的值):
function getAllEnumerableKeys(obj) {
const keys = [];
for (const key in obj) {
if (obj.hasOwnProperty(key)) continue; // 跳过自身属性,只留继承的
keys.push(key);
}
return keys;
}
// 示例
const parent = { a: 1, b: 2 };
const child = Object.create(parent);
child.c = 3;
console.log(getAllEnumerableKeys(child)); // ['a', 'b']
遍历所有层级的自有可枚举属性(含自身)
如果想手动控制、明确看到每层原型上的自有属性(即该层对象自己定义的、可枚举的属性),可用循环 + Object.getPrototypeOf + Object.keys:
- 从目标对象开始,每次调用
Object.getPrototypeOf(obj)获取其原型 - 对每个原型调用
Object.keys(proto)获取其**自有可枚举属性**(不含继承来的) - 遇到
null(即到达Object.prototype的原型)时停止
function getAllOwnEnumerableKeys(obj) {
const allKeys = new Set(); // 自动去重
let current = obj;
while (current !== null) {
Object.keys(current).forEach(key => allKeys.add(key));
current = Object.getPrototypeOf(current);
}
return Array.from(allKeys);
}
// 示例
const A = { x: 1 };
const B = Object.create(A); B.y = 2;
const C = Object.create(B); C.z = 3;
console.log(getAllOwnEnumerableKeys(C)); // ['z', 'y', 'x']
注意不可枚举属性和 Symbol 属性
Object.keys() 和 for...in 都**忽略不可枚举属性**(如 Object.prototype.toString)和所有 Symbol 属性。若需包含它们,得用:
-
Object.getOwnPropertyNames(obj)→ 获取所有自有属性名(含不可枚举) -
Object.getOwnPropertySymbols(obj)→ 获取自有 Symbol 属性 - 仍需配合原型链遍历,但要注意:内置不可枚举属性(如
toString)在Object.prototype上,Object.getOwnPropertyNames(Object.prototype)可拿到
实际建议:按需选择,别盲目遍历整个原型链
多数场景下,你并不需要“所有祖先节点的所有属性”。更常见的是:
- 判断某属性是否在原型链上存在:
obj.propertyIsEnumerable('key')或'key' in obj - 获取对象自身的可枚举属性:
Object.keys(obj) - 调试时快速查看继承关系:
console.dir(obj)在浏览器控制台中点开__proto__层级
手动遍历原型链容易陷入无限循环(比如意外修改了 __proto__ 指向自身),也违背封装原则。真有强需求,优先用 for...in 或明确限定层数(如只查到 Object.prototype 为止)。


















