浅谈深拷贝与浅拷贝
一开始看到深拷贝与浅拷贝我是完全一脸懵逼的,之前只在掘金的热门文章看到过,不过当时也没深究。
最近项目有个需求才让我真正认识深拷贝,项目需求是这样的,有一个用来存储element多选下拉框选中的值的数组,传往后台是后台需要字符串,于是机智的我立马加上一个join(',')
然后这边传
往后台,万万没想到,因为我用了element的rules
来验证表单是否非空的,将数组改成字符串之后验证就不通过了,因为element的验证还会验证数据的类型。然后我就准备用一个临时变量来存储表单数据,然后修改临时变量里面的数组(将其变成字符串),改好后准备再次提交时,又验证失败了,当时又一脸懵逼了,为什么这样也报错。于是百度了一翻知道了深拷贝。
1、 为什么修改临时变量form表单的数据也会修改?
下面来一个demo
1 |
|
看到这个例子,大多数人的答案肯定是arr是[1, 2, 3, 4]
,arr1是["a", 2, 3, 4]
,然而情况确是

再来一个demo
1 |
|
有了上面例子的经验,机智的小伙伴们一定以为输出的都是2,然而

简单粗暴的回答就是:数组是引用类型,修改数组中值会修改内存地址中的数据。
下面的详细的讲解以下:
1.1数据类型
javascript中有基本类型和引用类型两种数据类型
5种基本类型分别为Undefined、Null、Boolean、Number 和 String,变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。
引用类型为Function,Array,Object,是存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。
所以赋值时,基本类型是直接复制了一个新的变量,新旧两个变量之间没有关系;而引用类型也复制了新的变量,但这个变量是一个指针,新旧两个指针指向同一个对象,所以修改临时变量也会改动原有的form表单的数据
2、如果想实现项目里面的那个需求的话要怎么做?
2.1深拷贝的实现
2.1.1利用JSON对象来实现最简单的深拷贝
1 |
|
输出为
这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。
1 |
|
输出为
可以从中看出,obj中的普通对象和数组都能拷贝,然而date对象成了字符串,函数直接就不见了,正则成了一个空对象。
2.1.2 for in 实现深拷贝
1 |
|
输出为
3、拓展,浅拷贝的实现
3.1 最简单的浅拷贝
1 |
|
拷贝原对象的引用,这是最简单的浅拷贝。
3.2 Object.assign()
Object.assign是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
1 |
|
3.3 扩展运算符&解构赋值
1 |
|
4、深拷贝与浅拷贝的区别
浅拷贝是复制,两个对象指向同一个地址;
深拷贝是新开栈,两个对象指向不同的地址
| 和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据中包含子对象
:- | :- | :- | :-
赋值 | 是 | 改变会使原数据一起改变 | 改变会使原数据一起改变
浅拷贝 | 否 | 不会改变原数据 | 改变会使原数据一起改变
深拷贝 | 否 | 不会改变原数据 | 不会改变原数据
参考资料:
javaScript中浅拷贝和深拷贝的实现
JavaScript中的浅拷贝和深拷贝
再谈Javascript中的基本类型和引用类型(推荐)
JavaScript深拷贝的一些坑