JS继承中原型链共享引用类型属性会导致实例间状态污染,正确做法是在构造函数内初始化引用属性;避免直接重写prototype,应增量添加或修复constructor;关键原型需冻结防篡改,多环境场景下须隔离污染路径。

JS 继承中,原型链共享本身不是问题,问题出在**多个实例共用同一原型对象上的引用类型属性**,导致一处修改、处处生效。这不是设计缺陷,而是机制使然——关键在于如何规避误改、隔离状态、明确归属。
把引用类型属性移到构造函数里
原型上放方法没问题,但放数组、对象、函数等可变引用类型,就会被所有实例共享。比如父类原型上写 prototype.hobbies = [],子类任意实例 inst.hobbies.push('coding'),其他实例的 hobbies 也会变。
- 正确做法:在构造函数内部初始化引用属性,确保每个实例独有一份
- 例如:
function Animal(name) { this.name = name; this.hobbies = []; } - 这样即使
Animal.prototype.speak = function() {...}是共享的,hobbies也不会互相干扰
避免直接重写整个 prototype 对象
用 Child.prototype = { method() {} } 这种字面量赋值会彻底替换原型,不仅丢失 constructor,还清空所有已有方法(包括 toString、valueOf 等),破坏继承契约。
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
- 优先用
Object.assign(Child.prototype, { method() {} })增量添加 - 若必须重置,记得补回
constructor:Object.defineProperty(Child.prototype, 'constructor', { value: Child, writable: true, enumerable: false }) - 对关键原型如
Array.prototype或自定义基类原型,加载后立即Object.freeze(),阻止后续篡改
微前端等多环境场景下切断原型污染路径
主应用和子应用共用全局原型时,一个子应用执行 EventTarget.prototype.addEventListener = patchedFn,就会影响所有其他模块。
- 在子应用挂载前冻结敏感原型:
Object.freeze(EventTarget.prototype)、Object.freeze(Promise.prototype) - 拦截危险操作:临时覆盖
Object.defineProperty,当目标是Array.prototype或Object.prototype时直接跳过 - 不把基类挂到
window上,改用工厂函数或 props 注入,避免window.BaseClass = newVersion覆盖主应用版本
用 hasOwnProperty 区分自有属性与继承属性
判断某个属性是否属于当前实例本身,而不是从原型链上继承来的,能帮你快速识别“谁动了数据”。
-
obj.hasOwnProperty('hobbies')返回true,说明这个hobbies是实例自己加的,不是原型给的 - 结合
Object.keys(obj)只返回自有可枚举属性,适合做状态快照或深比较前的预处理 - 调试时打印
console.log(Object.getOwnPropertyNames(obj))和Object.getPrototypeOf(obj)对比,一眼看出属性来源层级

















