原型链上的属性在浅拷贝和深拷贝中默认都不会被复制,因标准方法只处理对象自身的可枚举属性,忽略继承属性;需用object.create()或new constructor()等方式显式保留原型链。

原型链上的属性在浅拷贝和深拷贝中**默认都不会被复制**,这是关键前提。JavaScript 的标准拷贝方法(如 Object.assign()、展开运算符、structuredClone())只操作对象自身的可枚举属性,完全忽略原型链上继承来的属性。
为什么原型链属性不参与拷贝
因为拷贝的本质是“创建新对象 + 复制源对象的自有属性”,而原型链属性属于继承关系,并非实例自身拥有。JS 引擎按需查找:读取时沿 __proto__ 向上找,但写入或复制时只作用于当前对象自身。
例如:
function Person() {}
Person.prototype.say = function() { return 'hi'; };
const p = new Person();
p.name = 'Alice';
const copy = { ...p }; // 浅拷贝
console.log(copy.name); // 'Alice' ✅(自有属性)
console.log(copy.say); // undefined ❌(原型方法未复制)
console.log(copy.__proto__ === Object.prototype); // true(新对象原型是 Object,不是 Person.prototype)
如何让拷贝结果保留原型链行为
真正需要的是“保持原型链继承关系”,而不是“把原型属性复制到实例上”。有几种实用方式:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
-
用
Object.create()显式设置原型:先创建空对象并指定原型,再拷贝自有属性
const copy = Object.assign( Object.create(Object.getPrototypeOf(p)), // 新对象原型 = p 的原型 p // 只拷贝 p 自身属性 );
这样 copy.say() 就能正常调用。
-
使用
new p.constructor()构造新实例:适合 class 实例,天然继承原型
class Person {
constructor(name) { this.name = name; }
greet() { return `Hello, ${this.name}`; }
}
const p = new Person('Bob');
const copy = Object.assign(new p.constructor(), p); // 原型链完整保留
- 避免依赖原型属性拷贝:如果业务逻辑要求拷贝后仍能访问 say/greet,说明设计上应把方法视为“共享行为”,而非必须复制的数据
特别注意不可枚举与 Symbol 属性
除了原型属性,以下也**不会被常规拷贝方法处理**:
- 通过
Object.defineProperty()设置的enumerable: false属性 - Symbol 类型的键(
Object.assign()和展开运算符均忽略,需用Reflect.ownKeys()手动遍历) - getter/setter 若未触发读取,其逻辑也不会被复制(
Object.assign()会触发 setter,展开运算符不会)
Vue 等响应式框架中的额外影响
在 Vue 2/3 的 data 或 reactive() 初始化时,若对含 getter/setter 的对象做浅拷贝(如 Object.assign({}, obj)),会丢失响应性——因为新对象没有经过响应式系统代理,且原型链断裂导致依赖收集失效。此时应使用框架提供的 toRaw() + reactive() 或专用克隆工具。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










