javascript深拷贝如何实现_有哪些常见的方法与陷阱?

发布时间 - 2026-01-05 00:00:00    点击率:
JSON.parse(JSON.stringify(obj))不能当深拷贝用,因会丢弃函数、undefined、Symbol等,且无法处理循环引用;structuredClone()是目前最靠谱的原生方案,但不支持函数、undefined、Symbol;手写需注意特殊类型识别与循环引用缓存;Lodash的_.cloneDeep()覆盖广但不深拷贝函数体、不保留原型。

JSON.parse(JSON.stringify(obj)) 为什么不能当深拷贝用?

它看起来最简单,但实际只适合纯数据对象——没有函数、undefined、Symbol、Date、RegExp、Map、Set、BigInt,也不能处理循环引用。JSON.stringify() 遇到这些值会静默丢弃或报错,比如:undefined 和函数直接被忽略,Date 变成字符串,NaN 变成 null

常见错误现象:

  • JSON.parse(JSON.stringify({ time: new Date() })){ time: "2025-01-01T00:00:00.000Z" }(不再是 Date 实例)
  • JSON.parse(JSON.stringify({ fn() {} })){}(函数消失)
  • const obj = {}; obj.self = obj; JSON.stringify(obj) → 报错 TypeError: Converting circular structure to JSON

structuredClone() 是目前最靠谱的原生方案吗?

是的,但它有明确的浏览器兼容性边界和类型限制。structuredClone() 支持 DateRegExpMapSetArrayBufferTypedArrayBigIntObjectArray 等,也支持循环引用,且保持原型链无关(即结果总是 plain object/array)。

使用注意点:

立即学习“Java免费学习笔记(深入)”;

  • 不支持函数、undefinedSymbol —— 遇到会抛错:DataCloneError: function is not supported
  • Node.js 17+ 默认启用,但需开启 --enable-structured-cloning 标志;Node.js 18.13+ 和 20.6+ 已默认启用
  • 浏览器中 Chrome 98+、Firefox 94+、Safari 16.4+ 支持;IE 完全不支持
const original = { date: new Date(), map: new Map([['a', 1]]), self: null };
original.self = original;
const cloned = structuredClone(original); // ✅ 成功,cloned.self 指向 cloned 自身

手写递归深拷贝时最容易漏掉什么?

不是“递归调用”本身,而是对特殊内置对象和边界类型的识别与分发。比如只判断 typeof obj === 'object' 会把 nullArrayDateRegExp 全部混为一谈。

关键判断逻辑应优先使用 Object.prototype.toString.call()

  • [object Array] → 用 map + 递归
  • [object Date]new Date(obj.getTime())
  • [object RegExp]new RegExp(obj)(注意 flags)
  • [object Map]new Map([...obj].map(([k, v]) => [k, deepClone(v)]))
  • null 要单独处理,否则 Object.keys(null) 报错
  • 循环引用必须缓存已拷贝的源对象映射,否则栈溢出
function deepClone(obj, seen = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (seen.has(obj)) return seen.get(obj);
  
  const tag = Object.prototype.toString.call(obj);
  let cloned;
  
  if (tag === '[object Array]') {
    cloned = [];
  } else if (tag === '[object Date]') {
    cloned = new Date(obj.getTime());
  } else if (tag === '[object RegExp]') {
    cloned = new RegExp(obj.source, obj.flags);
  } else {
    cloned = {};
  }
  
  seen.set(obj, cloned);
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      cloned[key] = deepClone(obj[key], seen);
    }
  }
  return cloned;
}

Lodash 的 _.cloneDeep() 真的能覆盖所有场景?

它比手写健壮得多,支持函数(浅拷贝函数引用)、undefinedSymbolErrorPromiseDOM 节点等,也处理循环引用。但要注意两点:

  • 它不会深拷贝函数体,只是复制引用 —— 这是设计选择,不是 bug
  • 它不 clone prototype 上的属性,也不保留构造器,结果始终是 plain object/array/类数组
  • 体积较大(约 15KB minzipped),如果项目已用 Webpack/Rollup,建议按需引入:import cloneDeep from 'lodash-es/cloneDeep';

真正容易被忽略的是:当你依赖某个库内部用了 _.cloneDeep(),而你传入了自定义类实例(如 class User {}),它只会拷贝可枚举属性,不会调用 User 的 constructor 或 getter/setter —— 这不是缺陷,是通用工具的合理取舍。


# javascript  # java  # js  # node.js  # json  # node  # 浏览器  # 工具  # safari  #   # ai  # 为什么 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何正确选择百度移动适配建站域名?  EditPlus中的正则表达式实战(5)  Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】  如何快速登录WAP自助建站平台?  Laravel如何实现数据导出到CSV文件_Laravel原生流式输出大数据量CSV【方案】  如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程  香港服务器部署网站为何提示未备案?  Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】  JS碰撞运动实现方法详解  Laravel的Blade指令怎么自定义_创建你自己的Laravel Blade Directives  Laravel观察者模式如何使用_Laravel Model Observer配置  如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?  Android中AutoCompleteTextView自动提示  Laravel怎么使用Intervention Image库处理图片上传和缩放  javascript基本数据类型及类型检测常用方法小结  如何在IIS中配置站点IP、端口及主机头?  详解jQuery停止动画——stop()方法的使用  Laravel如何使用Gate和Policy进行授权?(权限控制)  php打包exe后无法访问网络共享_共享权限设置方法【教程】  中山网站制作网页,中山新生登记系统登记流程?  iOS正则表达式验证手机号、邮箱、身份证号等  原生JS获取元素集合的子元素宽度实例  网站制作怎么样才能赚钱,用自己的电脑做服务器架设网站有什么利弊,能赚钱吗?  Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】  今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】  如何在IIS7上新建站点并设置安全权限?  Android Socket接口实现即时通讯实例代码  如何快速搭建高效可靠的建站解决方案?  如何用AI一键生成爆款短视频文案?小红书AI文案写作指令【教程】  香港代理服务器配置指南:高匿IP选择、跨境加速与SEO优化技巧  使用spring连接及操作mongodb3.0实例  如何在建站宝盒中设置产品搜索功能?  Laravel如何获取当前用户信息_Laravel Auth门面获取用户ID  Laravel怎么配置.env环境变量_Laravel生产环境敏感数据保护与读取【方法】  Laravel如何实现本地化和多语言支持?(i18n教程)  Laravel如何与Inertia.js和Vue/React构建现代单页应用  个人网站制作流程图片大全,个人网站如何注销?  Python企业级消息系统教程_KafkaRabbitMQ高并发应用  Laravel Vite是做什么的_Laravel前端资源打包工具Vite配置与使用  laravel怎么配置和使用PHP-FPM来优化性能_laravel PHP-FPM配置与性能优化方法  如何在阿里云高效完成企业建站全流程?  Laravel如何升级到最新的版本_Laravel版本升级流程与兼容性处理  Laravel Sail是什么_基于Docker的Laravel本地开发环境Sail入门  如何在腾讯云服务器上快速搭建个人网站?  Laravel项目怎么部署到Linux_Laravel Nginx配置详解  如何用景安虚拟主机手机版绑定域名建站?  如何快速搭建安全的FTP站点?  Laravel如何配置任务调度?(Cron Job示例)  Win11怎么设置虚拟桌面 Win11新建多桌面切换操作【技巧】  桂林网站制作公司有哪些,桂林马拉松怎么报名?