当前位置: 首页
前端开发
JavaScript深克隆实现方法 使用Object.create复制对象原型链

JavaScript深克隆实现方法 使用Object.create复制对象原型链

热心网友 时间:2026-05-06
转载

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

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

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

开门见山地说,Object.create(Object.getPrototypeOf(obj)) 这行代码,常被误认为是实现深克隆的捷径。但真相是,它仅仅创建了一个**继承原对象原型链的空壳**,既不复制任何属性,更谈不上深克隆。把它当作“深克隆方法”,是一个流传甚广的误解。

它实际做了什么

让我们拆解一下这行代码的执行过程,它其实只干了两件事:

  • 第一步,获取 obj 的原型对象(也就是 obj.__proto__Object.getPrototypeOf(obj) 指向的那个对象)。
  • 第二步,用这个原型对象作为新对象的 [[Prototype]],创建一个**全新的、空荡荡的对象**——注意,这个新对象没有任何自有属性。

结果就是:新对象和原对象在原型链上确实是“亲戚”,共享同一套继承体系。但原对象身上那些实实在在的数据属性(比如 {a: 1, b: {c: 2}} 里的 ab),一个都没被复制过去。新对象只是个空架子。

为什么它不能实现深克隆

那么,真正的深克隆到底要求什么?标准可不低:

  • 需要递归地复制所有自有属性,无论是可枚举的还是不可枚举的,甚至包括 Symbol 类型的属性。
  • 对嵌套的对象、数组,以及 Date、RegExp 这些内置类型,要有类型识别能力,进行针对性的拷贝。
  • 保持原型关系(既然你提到了“相同原型结构”,那这一点就是必须项)。
  • 妥善处理循环引用,否则递归过程很容易栈溢出。

回头再看 Object.create(...),它连最基础的属性复制这一步都没迈出去,后面的所有高级要求自然也就无从谈起了。

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

想达到目的,思路必须清晰:分两步走。先深拷贝所有属性值,再为拷贝结果设置正确的原型。一个比较稳妥的组合方案是:

  • 属性拷贝:使用 Object.getOwnPropertyDescriptors(obj) 获取对象所有的属性描述符。这个方法很强大,能拿到包括 getter/setter、可写性(writable)、可枚举性(enumerable)、可配置性(configurable)在内的完整信息。然后,用 Object.defineProperties(新对象, descriptors) 将这些属性定义到新对象上。
  • 原型设置:有两种方式。一是用 Object.setPrototypeOf(新对象, Object.getPrototypeOf(obj)) 事后设置;二是在用 Object.create 创建空对象时就传入原型(但切记,此时属性还是空的,需要后续补充)。
  • 递归处理值:这才是深克隆的核心。对每个属性值进行类型判断——普通对象或数组就递归克隆;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。从 4.17+ 版本开始,它默认就会保留原型,并且已经健壮地处理了各种边界情况,省心又可靠。
  • 如果确实需要手写实现,思路一定要清晰:将“原型继承”和“属性深拷贝”视为两个独立的职责,分别处理,不要试图用 Object.create 一步到位。
  • 最后,必须清醒地认识到,有些东西是无法真正深克隆的,比如函数、DOM 节点、Error 对象实例等。对于这些,通常只能进行浅拷贝引用,或者设计特殊的处理逻辑。
来源:https://www.php.cn/faq/2426544.html

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
TypeScript 类型推断与 JSDoc 实现代码静态防御指南

TypeScript 类型推断与 JSDoc 实现代码静态防御指南

利用TypeScript类型推断配合JSDoc注释,可在不改动JavaScript代码的前提下实现类型安全。通过独立类型定义文件精确描述复杂结构,使用字面量联合类型约束常量,并确保嵌套对象深层只读。JSDoc注释同时提供实时类型校验与清晰文档,实现编码防御与文档化合一。

时间:2026-05-06 16:31
CSS全局字体动态缩放教程clamp函数与变量应用详解

CSS全局字体动态缩放教程clamp函数与变量应用详解

实现全局字体动态缩放时,不能直接在clamp()函数内使用CSS变量,因其要求静态长度值。正确方法是将CSS变量作为缩放因子,通过calc()乘法与clamp()结合,例如calc(var(--scale-base)*clamp(1rem,4vw,1 5rem))。这样,修改变量即可全局调整字体大小,同时保持clamp()的响应式范围。需注意移动端视口缩放可

时间:2026-05-06 16:30
CSS选择器性能优化指南避免通配符与深层嵌套

CSS选择器性能优化指南避免通配符与深层嵌套

CSS选择器性能优化需避免通配符和深层嵌套。通配符强制匹配所有节点,难以缓存且影响渲染;深层嵌套选择器从右向左回溯匹配,路径越长开销越大。建议使用具体标签选择器、BEM命名或data属性替代,并借助开发者工具定位低效选择器,以提升渲染性能。

时间:2026-05-06 16:30
HTML按钮input标签type属性用法详解

HTML按钮input标签type属性用法详解

HTML中input标签的type=button按钮本身无默认行为,需通过JavaScript绑定事件。推荐使用addEventListener方法,避免将代码直接写在onclick属性中。按钮显示文字必须通过value属性设置。与button元素相比,input按钮不会意外提交表单,但button元素在语义、可访问性和样式扩展上更具优势,通常建议优先选用。

时间:2026-05-06 16:30
Bootstrap 5响应式可见性d-none d-md-flex用法详解

Bootstrap 5响应式可见性d-none d-md-flex用法详解

Bootstrap5中,d-none与d-md-flex的组合可实现元素默认隐藏,仅在中等及以上屏幕(≥768px)显示为flex布局。其原理在于d-none全局生效,而d-md-flex通过媒体查询在指定宽度覆盖前者。使用时需注意断点匹配,避免自定义CSS覆盖或视口测试错误。该组合适用于桌面端导航等需条件显示的组件。

时间:2026-05-06 16:30
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程