JavaScript中不存在循环原型链,引擎会拦截报错;需防范人为循环引用,如通过__proto__或prototype互相指向,应校验原型链、避免共享引用类型、使用structuredClone等安全克隆方式。

原型继承中根本不存在“循环原型链”,JavaScript 引擎会主动拦截并报错;真正要防的是人为造成的循环引用——也就是对象通过 __proto__、constructor.prototype 或普通属性互相指向,形成闭环。这种结构会让属性查找中断、深拷贝失败、甚至触发 RangeError。
禁止直接操作 __proto__ 和 prototype
手动赋值 obj.__proto__ = obj 或 obj.__proto__ = otherObj(而 otherObj 的原型链又回指 obj)是最常见的成因。应统一改用 Object.setPrototypeOf(),并在调用前做校验:
- 确保目标原型不等于当前对象:
proto !== obj - 确保目标原型不在该对象的现有原型链中:
!isInPrototypeChain(obj, proto)(可用Object.getPrototypeOf配合Set实现) - 避免在构造函数或初始化逻辑里动态设置原型,尤其不要把实例本身或它的父级原型设为子级的
__proto__
避免在原型上挂载可变引用类型
原型是共享的,一旦在 Parent.prototype.cache = [] 或 .config = {} 这类地方放数组、对象、Map 等引用值,所有实例就会共用同一份内存,极易因误操作形成隐式循环(比如某个实例把自身存进这个共享数组)。
- 把这类数据移到构造函数或类字段中初始化:
this.items = []或items = [] - 若需懒加载,用
get返回新对象:get options() { return { timeout: 3000 }; } - 检查所有
prototype属性,确认没有[]、{}、new Set()等可变值
深拷贝时跳过原型相关字段
老旧克隆工具递归处理 constructor.prototype 或未过滤 __proto__,容易让新旧对象原型互引。安全做法是:
- 优先使用原生
structuredClone(),它默认隔离原型,不复制__proto__、函数、Symbol 等 - 手写克隆逻辑时,显式忽略键名含
__proto__、constructor、prototype(及大小写变体)的属性 - 对函数、DOM 节点、Promise 等不可序列化类型,直接跳过或替换为占位符,不强行递归
运行时轻量检测(仅限调试/测试)
上线环境无需扫描,但在开发或 CI 阶段可加一层防护:
- 启动时对关键基类实例执行原型链遍历,用
Set记录已访问原型 - 一旦发现重复,立即告警并打印路径:
A → B → C → A - 冻结基础原型:
Object.freeze(Object.prototype),防止后续污染(注意 IE 不支持)

















