如何区分继承属性拷贝限制?为何Object.assign无法复制原型链上的原始方法?

2026-04-30 20:401阅读0评论SEO问题
  • 内容介绍
  • 相关推荐

本文共计657个文字,预计阅读时间需要3分钟。

如何区分继承属性拷贝限制?为何Object.assign无法复制原型链上的原始方法?

`Object.assign()` 不复制原型链上的属性,因为它仅遍历并复制对象自身的自有属性(即拥有 `own`、`enumerable` 属性的属性)。继承自原型的属性不属于自身属性,因此不会被复制。

它只读取对象自身的属性描述符

JavaScript 中每个对象都有一个内部属性 [[Prototype]],指向其原型。原型上的方法或属性是通过属性查找机制“访问到”的,但并非存在于该对象实例上。Object.assign 的实现逻辑等价于:

  • 调用 Object.keys(source) 获取所有可枚举的自有属性名(不包括原型链上的)
  • 对每个键,执行 target[key] = source[key]
  • Object.keys() 明确不包含继承属性、不可枚举属性、symbol 键(除非显式处理)

看个直观例子

下面创建一个带原型方法的对象:

const parent = { sayHi() { return 'Hello'; } };
const child = Object.create(parent);
child.name = 'Alice';

console.log(child.name); // 'Alice' → 自身属性,可枚举
console.log(child.sayHi()); // 'Hello' → 继承方法,但 child 本身没有 sayHi 键
console.log('sayHi' in child); // true(存在)
console.log(child.hasOwnProperty('sayHi')); // false(非自有)

const copy = Object.assign({}, child);
console.log(copy); // { name: 'Alice' } —— sayHi 消失了

这不是 bug,而是设计意图

Object.assign 的定位是“对象属性合并/克隆”,不是“完整对象结构重建”。它的行为与以下操作一致:

  • for...in 循环会遍历继承属性,但 Object.assign 不会
  • Object.getOwnPropertyNames() 能拿到不可枚举自有属性,但 Object.assign 也不处理它们
  • 它专注的是“可被 JSON 序列化”那一类常规数据属性,而非运行时行为载体(如原型方法)

如果真需要拷贝原型行为,得换思路

原型方法本质是共享逻辑,通常不该“拷贝”,而应保持继承关系。若确实要让副本拥有相同能力,有几种合理做法:

  • 让副本也继承同一原型:Object.setPrototypeOf(copy, Object.getPrototypeOf(child))
  • 手动提取并赋值:copy.sayHi = child.sayHi(仅适用于已知方法名)
  • 使用 Object.getOwnPropertyDescriptors(child) + Object.defineProperties(),但这仍不处理原型,只强化自有属性控制

本文共计657个文字,预计阅读时间需要3分钟。

如何区分继承属性拷贝限制?为何Object.assign无法复制原型链上的原始方法?

`Object.assign()` 不复制原型链上的属性,因为它仅遍历并复制对象自身的自有属性(即拥有 `own`、`enumerable` 属性的属性)。继承自原型的属性不属于自身属性,因此不会被复制。

它只读取对象自身的属性描述符

JavaScript 中每个对象都有一个内部属性 [[Prototype]],指向其原型。原型上的方法或属性是通过属性查找机制“访问到”的,但并非存在于该对象实例上。Object.assign 的实现逻辑等价于:

  • 调用 Object.keys(source) 获取所有可枚举的自有属性名(不包括原型链上的)
  • 对每个键,执行 target[key] = source[key]
  • Object.keys() 明确不包含继承属性、不可枚举属性、symbol 键(除非显式处理)

看个直观例子

下面创建一个带原型方法的对象:

const parent = { sayHi() { return 'Hello'; } };
const child = Object.create(parent);
child.name = 'Alice';

console.log(child.name); // 'Alice' → 自身属性,可枚举
console.log(child.sayHi()); // 'Hello' → 继承方法,但 child 本身没有 sayHi 键
console.log('sayHi' in child); // true(存在)
console.log(child.hasOwnProperty('sayHi')); // false(非自有)

const copy = Object.assign({}, child);
console.log(copy); // { name: 'Alice' } —— sayHi 消失了

这不是 bug,而是设计意图

Object.assign 的定位是“对象属性合并/克隆”,不是“完整对象结构重建”。它的行为与以下操作一致:

  • for...in 循环会遍历继承属性,但 Object.assign 不会
  • Object.getOwnPropertyNames() 能拿到不可枚举自有属性,但 Object.assign 也不处理它们
  • 它专注的是“可被 JSON 序列化”那一类常规数据属性,而非运行时行为载体(如原型方法)

如果真需要拷贝原型行为,得换思路

原型方法本质是共享逻辑,通常不该“拷贝”,而应保持继承关系。若确实要让副本拥有相同能力,有几种合理做法:

  • 让副本也继承同一原型:Object.setPrototypeOf(copy, Object.getPrototypeOf(child))
  • 手动提取并赋值:copy.sayHi = child.sayHi(仅适用于已知方法名)
  • 使用 Object.getOwnPropertyDescriptors(child) + Object.defineProperties(),但这仍不处理原型,只强化自有属性控制