Js
包含基本数据类型与引用数据类型两种不同的数据类型的值,深拷贝与浅拷贝的概念只存在于引用数据类型。对于引用类型,浅拷贝是拷贝了指向这个对象堆内存的指针,是拷贝了对原对象引用,深拷贝是拷贝了该对象的所有属性到一个新的对象,若是原对象的某属性依然引用了其他对象,那么需要将原对象引用的其他对象一并进行深拷贝,并不断递归进行。对于基本数据类型是不存在深拷贝与浅拷贝的概念的,如果将一个基本数据类型变量的值赋值到另一个变量,那么新变量的值是对于原变量值的复制而不是引用,如果必须要按照深浅拷贝的概念理解的话,对于基本数据类型的复制可以理解为按值深拷贝。
原生方法实现浅拷贝,可以使用{...obj}
与Object.assign({}, obj)
等方式,{...obj}
主要是使用了Spread
操作符将对象表达式展开构造字面量对象的方式实现浅拷贝,对于Object.assign({}, obj)
是执行了一次将可枚举属性复制到目标对象并返回目标对象的操作。关于Object.assign
是浅拷贝还是对于第一层是深拷贝之后是浅拷贝的说法,主要取决于如何理解浅拷贝与深拷贝的概念,假如同本文一样认为只有引用类型才有浅拷贝与深拷贝的概念的话,那么Object.assign
就是浅拷贝;假如认为对于基本数据类型也有浅拷贝与深拷贝的概念的话,那么如上文所述对于基本数据类型的拷贝可以理解为按值深拷贝,那么关于Object.assign
第一层是深拷贝,第二层及以后是浅拷贝的说法也是没有问题的。
原生方法实现深拷贝,主要是使用JSON.parse()
与JSON.stringify()
,首先将对象序列化为JSON
字符串,再将JSON
字符串反序列化为对象,使用这种方式效率比较高,但是会有一些问题,对于循环引用的对象无法实现深拷贝,对于被拷贝的对象,如果对象中有属性为Date
对象,此种方式深拷贝会将时间对象转化为字符串;如果对象中有属性为RegExp
对象、Error
对象,此种方式深拷贝会得到一个空对象;如果对象中有属性为function
对象、undefined
、Symbol
值,此种方式深拷贝会忽略这些值;如果对象中有属性为NaN
、Infinity
、-Infinity
,此种方式深拷贝会将结果转为null
。
对于浅拷贝,只需要处理被拷贝对象的所有的可枚举属性进行赋值即可。
对于深拷贝,需要将基本数据类型进行赋值,然后对对象属性进行递归处理。