JavaScript 深拷贝和浅拷贝区别
你有没有过复制手机相册的经历?不小心点了“快速复制”,结果删了原照片,副本也一起没了;而“新建副本”就能完全独立,删哪个都不影响。其实JavaScript里的变量复制,也分这两种情况,就是咱们常说的浅拷贝和深拷贝。
先搞懂:为啥普通赋值不行?
先从简单的变量说起,比如存数字的let a = 5; let b = a,改b为6,a还是5——这是因为数字、字符串这类“基础数据”,是直接存在变量的“小抽屉”里,复制时真的把抽屉里的东西递了一遍。

但如果是存多个值的“复杂数据”,比如数组、对象,它们不是直接存在变量里,变量只拿了一个“地址条”——就像外卖取餐码,不是餐本身,是告诉你餐放在哪个编号的柜子里。这时候用let arr1 = [1,2,3]; let arr2 = arr1,复制的只是取餐码,两个取餐码对应同一个柜子,改arr2[0] = 99,你会发现arr1的第一个元素也变了,这就是为啥新手总疑惑“我复制的数组怎么跟着原数组变”。
浅拷贝:复制地址,但不拆“嵌套柜子”
浅拷贝就是做了个新的取餐码,放在新柜子里,但如果原柜子里还有小柜子(比如数组套数组、对象套对象),小柜子的取餐码还是和原小柜子共享。
举个例子:你有个快递箱(原对象),里面放了一份文件(基础数据)和一个小收纳盒(嵌套对象)。浅拷贝相当于你重新买了个一模一样的快递箱,文件直接抄了一份,小收纳盒只是拿了个同款取件码——你把浅拷贝出来的收纳盒里的便签改了,原收纳盒的便签也会变,但你把浅拷贝里的文件换成别的,原快递箱的文件不会动。
JavaScript里常用的浅拷贝方法有Array.prototype.slice()、Object.assign(),还有展开运算符...,都是只处理一层数据的复制。
深拷贝:连嵌套的“小柜子”都重新做一份
深拷贝就是彻底做了一套全新的东西,从外到内的柜子都重新买,每个物品都单独放,完全独立。
还是刚才的快递箱例子:深拷贝相当于你重新买了个快递箱,文件重新打印,小收纳盒也重新买了一个,连收纳盒里的便签都重新抄了一遍——改浅拷贝里任何一样东西,原快递箱都不会有变化。
比如用JSON.parse(JSON.stringify())可以做简单的深拷贝,能处理大部分日常场景,但要注意它有坑:如果数据里有函数、undefined、正则表达式,会被直接忽略;如果有循环引用(A对象里套了B,B又套了A),这个方法还会报错。更靠谱的是用专门的深拷贝函数,或者现代JS里的structuredClone(),这个浏览器原生的方法能处理大部分特殊数据类型,比如日期、二进制数据,不用自己写复杂逻辑。
一句话分清:啥时候用哪种拷贝?
如果复制的只是纯数字、字符串这类没嵌套的简单数据,随便赋值就行;如果是只有一层的数组/对象,用浅拷贝够快;如果是有多层嵌套的数组、对象,必须用深拷贝,不然改一个地方全跟着变,出了bug还找不到原因。
其实不管是浅拷贝还是深拷贝,核心都是看你要的“副本”和“原数据”是不是完全独立——就像你要给朋友发照片,发原文件怕他改了自己看不到备份,发新建副本就没这个顾虑,选对拷贝方式,就能少踩很多JS的小坑。