说起拷贝,那我们先看看JavaScript中复制变量值时会产生什么问题?
复制变量值
ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。理解它们的区别可以参考上一篇博客数据类型(基本类型和引用类型的定义与检测)
- 复制基本类型的值:
如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到新变量分配的位置上。来看一个例子:
var num1 = 5;
var num2 = num1;
num1中保存的值是5。当使用num1的值来初始化num2时,num2中也保存了值5。但num2中的5和num1中的5是完全独立
的,该值只是num1中5的一个副本。此后,这两个变量可以参与任何操作而不会相互影响。下图展示了复制基本类型值的过程。
3. 复制 引用类型的值:
如果从一个变量向另一个变量复制引用类型的值,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个 指针,而这个指针指向存储在堆中的一个对象。复制操作结束后, 两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量,如下面的例子所示:
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Anna";
alert(obj2.name); // "Anna"
首先,变量obj1保存了一个对象的实例。然后,这个值被复制到了obj2中,即obj1和obj2都指向同一个对象。这样,当为obj1添加name属性后,可以通过obj2来访问这个属性,因为这两个变量引用的都是同一个对象。下图展示了保存在变量对象中的变量和保存在堆中的对象之间的关系。
对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部数据。JavaScript中有两种类型的对象拷贝:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。
深拷贝与浅拷贝
- 浅拷贝:只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅拷贝仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅拷贝出来的对象也会相应改变。
- 深拷贝:在计算机中开辟了一块新的内存地址用于存放复制的对象。
深拷贝和浅拷贝最根本的区别
在于是否是真正获取了一个对象的复制实体,而不是引用。
实现深拷贝
1.使用递归的方式实现深拷贝
//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
2.通过 JSON 对象实现深拷贝
//通过js的内置对象JSON来进行数组对象的深拷贝
function deepClone2(obj) {
var _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone;
}
问题: 无法实现对对象中方法的深拷贝
3.通过jQuery的extend方法实现深拷贝
var array = [1,2,3,4];
var newArray = $.extend(true,[],array);
4.Object.assign()拷贝
let target=[];
let testArr=[2,3,5,8];
Object.assign(target,testArr);
console.log(target);
testArr.push(8);
console.log("我是原来的"+target+",我是现在的"+testArr);
当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。
5.lodash函数库实现深拷贝
lodash很热门的函数库,提供了 lodash.cloneDeep()实现深拷贝