javascript原型链本身不会导致循环引用报错,真正报错的是json.stringify、深拷贝或手动遍历等操作在处理人为构造的循环原型(如a.__proto__=b且b.__proto__=a)时引发的异常。

JavaScript 中原型链本身不会导致循环引用报错。所谓“原型链循环引用报错”,其实是对现象的误解——真正报错的通常是 JSON.stringify、深拷贝函数或手动遍历逻辑在处理具有循环原型链的对象时触发的异常,而非原型链机制自身出错。
原型链天然支持单向继承(A.__proto__ = B,B.__proto__ = C),但若人为构造 A.__proto__ = A 或 A.__proto__ = B && B.__proto__ = A,就形成了原型链上的循环。这种结构虽不被语言禁止,却会在某些操作中引发问题。
为什么原型链循环会出问题?
-
JSON.stringify(obj)遇到原型链循环时,可能间接访问到循环引用的属性(尤其配合replacer或自定义toJSON)而报TypeError: Converting circular structure to JSON; - 手写深拷贝或递归遍历时,若未检查
__proto__或Object.getPrototypeOf()的返回值,会陷入无限递归; -
Object.assign()、扩展运算符等不处理原型链,所以它们本身不报错,但也会忽略原型上方法,造成行为偏差。
如何安全处理原型链中的循环?
✅ 检测并阻止原型链循环
用 WeakSet 跟踪已访问的原型对象,防止重复遍历:
function hasCircularPrototype(obj) {
const seen = new WeakSet();
let current = obj;
while (current !== null && current !== undefined) {
if (seen.has(current)) return true;
seen.add(current);
current = Object.getPrototypeOf(current);
}
return false;
}
// 示例:人为制造循环原型
const a = {};
const b = {};
a.__proto__ = b;
b.__proto__ = a;
console.log(hasCircularPrototype(a)); // true
✅ 序列化时跳过原型属性(推荐做法)
避免让 JSON.stringify 接触原型链,只序列化自身可枚举属性:
function safeStringify(obj) {
const plain = {};
for (const key of Object.keys(obj)) {
plain[key] = obj[key];
}
return JSON.stringify(plain);
}
注意:
Object.keys()不含原型属性,也不含不可枚举或 Symbol 属性,适合大多数数据导出场景。
✅ 深拷贝时控制是否复制原型链
使用 rfdc 等库可显式开启/关闭原型链处理:
const rfdc = require('rfdc');
const cloneWithProto = rfdc({ proto: true, circles: true });
const Parent = { say() { return 'hi'; } };
const child = Object.create(Parent);
child.value = 42;
const copy = cloneWithProto(child);
console.log(copy.say()); // "hi" —— 原型方法保留
console.log(copy.__proto__ === Parent); // true
若不用第三方库,手写拷贝时需单独处理 __proto__:
function cloneWithProto(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const cloned = Array.isArray(obj) ? [] : {};
// 复制自身属性
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = cloneWithProto(obj[key]);
}
}
// 复制原型(谨慎!仅当明确需要)
const proto = Object.getPrototypeOf(obj);
if (proto && proto !== Object.prototype) {
Object.setPrototypeOf(cloned, proto);
}
return cloned;
}
⚠️ 不要这样做
- 直接修改
Object.prototype或Function.prototype并设为可枚举(如Object.defineProperty(Object.prototype, 'x', { value: 1, enumerable: true })),会导致所有对象的for...in、JSON.stringify等行为异常,且极易引发循环遍历; - 在
toJSON()方法里返回this或未过滤地展开this,可能把原型链带入序列化路径。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











