原型对象不可深拷贝,真正需深拷贝的是实例中覆盖或修改的继承属性;应提取原型自有可枚举属性并深拷贝为实例自有属性,避免共享引用导致污染。

原型对象本身不是用来“深拷贝”的目标,而是属性的来源。真正需要深拷贝的是实例对象,尤其是它从原型继承来、又被自身覆盖或修改的属性——如果只做浅拷贝,新对象仍可能通过原型链间接影响原对象,或因共享嵌套引用造成污染。
明确:原型上的属性通常不参与深拷贝
JavaScript 中,原型(prototype)是共享的,不可被“拷贝”到实例内部。Object.create()、class 继承、或直接赋值 prototype 都不会把原型上的方法或数据复制成实例自有属性。所以所谓“深拷贝原型对象”,实际要解决的是两个问题:
- 把原型链上可枚举的自有属性(比如构造函数中挂载的默认配置对象)提取出来,再深拷贝成实例的自有属性;
- 避免实例修改嵌套对象时,意外改到原型上同名的引用类型值(例如原型里有个
defaults = { filter: { price: 0 } },实例直接改this.defaults.filter.price就会污染所有实例)。
安全提取并深拷贝原型默认值
常见于组件类、配置构造器等场景。不要直接用 Object.assign({}, Constructor.prototype.defaults),这仍是浅拷贝:
- 如果
defaults是普通对象,推荐用structuredClone()(现代浏览器 & Node.js 17.0+):const config = structuredClone(Constructor.prototype.defaults); - 需兼容旧环境,且
defaults不含函数/Date/undefined/Symbol 等,可用 JSON 方案:const config = JSON.parse(JSON.stringify(Constructor.prototype.defaults)); - 若结构复杂(含 Date、Map、循环引用等),建议封装一个带 WeakMap 的递归深拷贝函数,显式跳过原型链遍历,只处理传入对象自身可枚举属性。
避免原型污染的关键习惯
深拷贝只是手段,根子在设计。以下做法比“拷贝原型”更有效:
- 原型上只放不可变默认值(如字符串、数字、冻结对象
Object.freeze({ a: 1 })),不放可变嵌套对象; - 实例初始化时,用深拷贝创建自己的配置副本:
this.config = deepClone(Constructor.prototype.config || {}); - 永远不直接修改原型上的对象属性,例如禁止写
MyClass.prototype.options.filters.push(...); - 用
Object.getOwnPropertyDescriptors()+Object.defineProperties()可以精确复制自有属性(不含原型),再对每个属性值按类型深拷贝。
一个实用小工具函数示例
用于安全提取并深拷贝原型上的默认配置(仅处理自有可枚举属性,排除函数和 undefined):
(支持 Date、RegExp、Array、Object,自动防循环引用)function clonePrototypeProps(ctor) {<br> const proto = ctor.prototype;<br> const descriptors = Object.getOwnPropertyDescriptors(proto);<br> const result = {};<br> const seen = new WeakMap();<br><br> function clone(val) {<br> if (val === null || typeof val !== 'object') return val;<br> if (seen.has(val)) return seen.get(val);<br> if (val instanceof Date) return new Date(val);<br> if (val instanceof RegExp) return new RegExp(val);<br> if (Array.isArray(val)) {<br> const arr = [];<br> seen.set(val, arr);<br> for (let i = 0; i return arr;<br> }<br> const obj = {};<br> seen.set(val, obj);<br> for (const key in val) {<br> if (Object.prototype.hasOwnProperty.call(val, key)) {<br> obj[key] = clone(val[key]);<br> }<br> }<br> return obj;<br> }<br><br> for (const key in descriptors) {<br> if (descriptors[key].enumerable && descriptors[key].value !== undefined) {<br> result[key] = clone(descriptors[key].value);<br> }<br> }<br> return result;<br>}











