别再滥用JSON.parse深拷贝了!一文看懂structuredClone的正确用法与避坑指南

简介:还在用JSON.parse(JSON.stringify())做深拷贝?小心Date变字符串、函数丢失、循环引用导致页面白屏!本文深度解析JSON深拷贝的致命缺陷,并带你掌握原生深拷贝神器structuredClone,轻松搞定99%的前端数据拷贝需求,告别线上事故。

上周同事遇到一个神坑问题,页面数据改了,结果原始数据也跟着变了,找了半天原因,最后发现是深拷贝没写对。他用的就是那个号称“一行代码搞定深拷贝”的写法:

const newData = JSON.parse(JSON.stringify(oldData))

你是不是也觉得这玩意儿挺好用的?我以前也这么觉得,直到它差点把我的项目搞崩了。

这行代码到底有什么毛病?

咱们先看一个最简单的例子。假设你有个订单数据,里面有个下单时间:

const order = {  id: 1001,  price: 299,  createTime: new Date('2025-06-01')}
const copy = JSON.parse(JSON.stringify(order))console.log(copy.createTime)  // 猜猜输出什么?

你以为是 Date 对象?太天真了。输出的是 "2025-06-01T00:00:00.000Z",一个字符串!

这意味着你后面如果想调 getMonth()getFullYear() 这些日期方法,直接报错给你看。

更离谱的事情还在后面

再看个例子,你有一个配置对象,里面有个重置函数:

const config = {  
    theme: 'dark',  
    reset: function() {    
        this.theme = 'light'  
    },  
    log: () => console.log('config loaded')
}
const copy = JSON.parse(JSON.stringify(config));
console.log(copy)  // 输出:{ theme: "dark" }

什么?resetlog 直接消失了?没错,函数会被无情丢弃。

undefined 呢?

const data = {  name: '阿浪',  age: undefined,  hobby: null}
const copy = JSON.parse(JSON.stringify(data));
console.log(copy)  // { name: "阿浪", hobby: null }

age 这个字段整个被删掉了,连个招呼都不打。

还有 Symbol 类型的值,也一样会被直接忽略。

真正致命的一击:循环引用

大多数人在用这个方法时,压根不知道它还有个“定时炸弹”——循环引用。

啥是循环引用?就是对象里的某个属性,直接或间接地引用了自己。

const me = {  name: '阿浪',  friend: null}
me.friend = me  // 自己引用自己
// 这一行会直接报错!
const copy = JSON.parse(JSON.stringify(me));
// Uncaught TypeError: Converting circular structure to JSON

项目里如果出现了循环引用(比如树形结构、双向链表、父子组件传参),整个页面就会直接崩溃。而你在报错堆栈里看到的,只有一行尴尬的 JSON.stringify

我当时就是因为这个,线上出了事故——后台返回的数据里有个递归引用,我随手用 JSON.parse(JSON.stringify(...)) 一拷,页面白屏了。用户反馈说“点一下就没了”,那感觉,啧,懂的都懂。

那到底应该怎么深拷贝?

先别急着说自己写一个递归函数,太麻烦了,而且还有类型判断的各种坑(比如数组和对象要区分,正则要特殊处理)。

其实浏览器早就给我们准备了一个原生深拷贝方法,名字很直白——structuredClone(结构化克隆)。

const original = {  
    name: '阿浪',  
    birthday: new Date('1995-03-15'),  
    tags: ['前端', '老狗'],  
    // 处理循环引用!  
    self: null
}
original.self = original
const copy = structuredClone(original);
console.log(copy.birthday instanceof Date);
// true,还是Date对象!
console.log(copy.self === copy);
 // true,循环引用也完美保留

你看,Date 对象保住了,MapSetRegExp 甚至 ArrayBuffer 都能正确拷贝。而且它原生支持循环引用,再也不会崩溃了。

我们来和 JSON 方案做个对比

特性 JSON.parse(JSON.stringify(...)) structuredClone
Date 对象 ❌ 变成字符串 ✅ 保留为 Date
函数 / undefined / Symbol ❌ 直接丢失 ⚠️ 会抛出异常(无法拷贝)
Map / Set / RegExp ❌ 变成空对象或丢失 ✅ 正确拷贝
循环引用 ❌ 报错崩溃 ✅ 支持
性能 一般 较好(底层原生实现)

注意:structuredClone 也不是万能的。它不能拷贝函数、不能拷贝 DOM 元素、不能拷贝某些内置对象(比如 Error、Promise)。遇到这些情况,它也会抛错。

不过呢,在 99% 的前端业务场景里——拷贝后端返回的 JSON 数据、拷贝组件 state、拷贝配置对象——它已经完全够用了。

structuredClone 是 2022 年左右全面支持的,现在主流浏览器(Chrome 98+、Firefox 94+、Safari 15.4+、Edge 98+)都已经原生支持了。Node.js 从 17.0.0 开始也支持(需要 --experimental-global 标志,后来直接内置)。

如果你还在兼容 IE 或者老版本安卓 WebView,那没办法,只能用 lodash 的 _.cloneDeep 或者自己造轮子了。但说真的,2026 年还死磕 IE 的项目,应该不多了吧?

编程经验共享公众号二维码
更多内容关注公众号
Copyright © 2021 编程经验共享 赣ICP备2021010401号-1