如何利用 Object.create Object.getPrototypeOf 实现具备相同原型结构的深克隆

作者:袖梨 2026-06-07
Object.create(Object.getPrototypeOf(obj))仅创建继承原对象原型的空对象,不复制任何属性,因此不是深克隆。真正保留原型的深克隆需先递归拷贝所有属性(含symbol、getter/setter等),再设置新对象原型为原对象原型。

Object.create(Object.getPrototypeOf(obj)) 本身只能创建一个**新对象并继承原对象的原型链**,它不复制属性,更不是深克隆。把它当作“深克隆方法”是一种常见误解。

它实际做了什么

这行代码只完成两件事:

  • 获取 obj 的原型对象(即 obj.__proto__ 或 Object.getPrototypeOf(obj))
  • 用这个原型对象作为新对象的 [[Prototype]],创建一个**空的新对象**(没有自有属性)

结果是:新对象和原对象共享同一原型链,但所有数据属性(如 {a: 1, b: {c: 2}} 中的 ab)都完全没被复制——新对象是空的。

为什么它不能实现深克隆

深克隆要求:

  • 递归复制所有可枚举/不可枚举的自有属性(包括 symbol 属性)
  • 对嵌套对象、数组、日期、正则等内置类型做类型感知的拷贝
  • 保持原型关系(可选,但你提到了“相同原型结构”,所以这点需保留)
  • 处理循环引用(否则会爆栈)

Object.create(...) 连最基础的属性复制都没做,自然不满足任何一条。

如何真正实现“保留原型的深克隆”

需要分两步:先深拷贝属性值,再把结果对象的原型设为原对象的原型。推荐组合使用:

  • 属性拷贝:用 Object.getOwnPropertyDescriptors(obj) 获取所有属性描述符(含 getter/setter、writable、enumerable、configurable),再用 Object.defineProperties(新对象, descriptors)
  • 原型设置:用 Object.setPrototypeOf(新对象, Object.getPrototypeOf(obj)) 或在 Object.create 创建时传入(但注意:create 只设 [[Prototype]],不设属性)
  • 递归处理值:对每个属性值判断类型——普通对象/数组递归克隆;Date、RegExp、Map、Set 等需特殊构造;基本类型直接返回;null 和 undefined 不处理;遇到循环引用要缓存已克隆对象

简单示意(无循环引用处理):

function cloneWithPrototype(obj) {  if (obj === null || typeof obj !== 'object') return obj;  // 创建空对象,原型设为 obj 的原型  const cloned = Object.create(Object.getPrototypeOf(obj));  // 获取所有自有属性描述符  const descriptors = Object.getOwnPropertyDescriptors(obj);    // 逐个克隆属性值并定义到新对象  for (const key in descriptors) {    const desc = descriptors[key];    if ('value' in desc) {      desc.value = cloneWithPrototype(desc.value); // 递归克隆值    }    Object.defineProperty(cloned, key, desc);  }  return cloned;}

更实用的建议

  • 日常开发中,优先使用成熟库如 lodash.cloneDeep,它默认保留原型(v4.17+),且健壮处理各种边界
  • 若必须手写,不要从 Object.create 入手想“一步到位”,而应明确分离“原型继承”和“属性深拷贝”两个职责
  • 注意:函数、DOM 节点、Error 实例等无法真正深克隆,只能浅拷贝引用或特殊处理

相关文章

精彩推荐