在JavaScript中,每个对象都有对应的原型对象,对象通过__proto__属性或者Object.getPrototypeOf方法向上查找原型,形成原型链。正常情况下原型链的终点是null,但如果某个对象的原型指向了链中已经出现过的对象,就会产生循环引用,导致属性查找时无限循环。

原型链循环引用的常见场景
循环引用通常出现在手动修改原型指向的场景中,比如下面这种错误写法:
// 错误示例:构造循环原型链
function A() {}
function B() {}
A.prototype = new B();
B.prototype = new A(); // 此时A和B的原型互相指向,形成循环
const a = new A();
// 访问a的属性时,会沿着A实例 -> B实例 -> A.prototype -> B.prototype -> A.prototype...无限循环
判断原型链是否有循环引用的核心思路
判断的核心逻辑是遍历对象的原型链,同时记录已经访问过的原型对象,如果遍历过程中遇到了已经记录过的对象,就说明存在循环引用。具体步骤如下:
- 初始化一个集合,用来存储遍历过程中遇到的所有原型对象
- 从当前对象的原型开始,依次向上获取原型对象
- 每次获取原型后,检查该原型是否已经在集合中,如果在则说明有循环,如果不在就加入集合
- 如果原型为null,说明遍历到了原型链终点,没有循环
具体实现方法
方法一:使用Set存储原型对象
Set可以自动去重,适合用来记录已经访问过的原型,实现代码如下:
function hasPrototypeCycle(obj) {
// 处理非对象或者null的情况
if (obj === null || typeof obj !== 'object') {
return false;
}
const visitedPrototypes = new Set();
let currentProto = Object.getPrototypeOf(obj);
while (currentProto !== null) {
// 如果当前原型已经在集合中,说明存在循环
if (visitedPrototypes.has(currentProto)) {
return true;
}
// 把当前原型加入集合
visitedPrototypes.add(currentProto);
// 继续向上获取原型
currentProto = Object.getPrototypeOf(currentProto);
}
// 遍历到null,没有循环
return false;
}
方法二:使用数组存储并比较引用
如果不想使用Set,也可以用数组存储原型,通过includes方法判断是否出现过,代码如下:
function hasPrototypeCycle(obj) {
if (obj === null || typeof obj !== 'object') {
return false;
}
const protoList = [];
let currentProto = Object.getPrototypeOf(obj);
while (currentProto !== null) {
if (protoList.includes(currentProto)) {
return true;
}
protoList.push(currentProto);
currentProto = Object.getPrototypeOf(currentProto);
}
return false;
}
方法验证
我们用之前的循环原型链示例来验证方法是否有效:
function A() {}
function B() {}
A.prototype = new B();
B.prototype = new A();
const a = new A();
console.log(hasPrototypeCycle(a)); // 输出true,存在循环引用
// 正常对象的原型链检测
const normalObj = {};
console.log(hasPrototypeCycle(normalObj)); // 输出false,无循环
注意事项
需要注意几个边界情况:
- 传入的参数如果不是对象或者是null,直接返回false,因为基础类型没有原型链
- Object.getPrototypeOf是标准方法,比直接使用__proto__兼容性更好,推荐优先使用
- 如果原型链非常长,遍历过程的时间复杂度是O(n),n是原型链长度,属于可接受范围
原型链循环引用在常规开发中很少见,但如果涉及到动态修改原型、实现自定义继承逻辑时,建议提前用上述方法做检测,避免出现难以排查的运行时错误。
JavaScript原型链循环引用原型判断修改时间:2026-07-14 23:03:20