前言
基本类型和引用类型
ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置。
而引用类型值是指那些保存堆内存中的对象,意思是变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,该位置保存对象。
目前基本类型有:
Boolean、Null、Undefined、Number、String、Symbol(ES6才新出来)
引用类型有:
Object、Array、Function
理解了基本类型和引用类型之后,我们来写一个栗子
var obj1 = {x:1,y:2};
var obj2 = obj1;
console.log(obj1); // {x:1,y:2}
console.log(obj2); // {x:1,y:2}
obj2.x = 3;
console.log(obj1); // {x:3,y:2}
console.log(obj2); // {x:3,y:2}
console.log(obj2 === obj1); // true
上面我们创建了一个对象,分别赋值给obj1和obj2,然后你修改obj1的值,发现obj2的值也跟着改变了。
归根结底我们创建的对象只有一份,只是用2个指针去引用了而已。这就是我们所说的浅拷贝。
浅拷贝
我们来看几个例子
Array
var arr1 = [1, 2], arr2 = arr1.slice();
console.log(arr1); //[1, 2]
console.log(arr2); //[1, 2]
arr2[0] = 3; //修改arr2
console.log(arr1); //[1, 2]
console.log(arr2); //[3, 2]
console.log(arr1 == arr2) //false
我们可以看到利用Array的slice方法进行生产一个新的数组,也就是引用的地址不一样了,会达到深拷贝的效果,这是真的吗?我们在看一下下边的栗子:
var arr1 = [1, 2, [3, 4]], arr2 = arr1.slice();
console.log(arr1); //[1, 2, [3, 4]]
console.log(arr2); //[1, 2, [3, 4]]
arr2[2][1] = 5;
console.log(arr1); //[1, 2, [3, 5]]
console.log(arr2); //[1, 2, [3, 5]]
console.log(arr1 == arr2) //false
我们可以看到这个api只能对第一层的嵌套进行了深拷贝,对于深层次的引用并没有进行深拷贝,等于这不是真正意义上的深拷贝。
具备同等特性的还有:concat、Array.from()
Object
我们在开发中经常使用Object.assign方法,其实这也是一个浅拷贝的方法
var obj1 = {x: 1, y: 2}, obj2 = Object.assign({}, obj1);
console.log(obj1) //{x: 1, y: 2}
console.log(obj2) //{x: 1, y: 2}
obj2.x = 2; //修改obj2.x
console.log(obj1) //{x: 1, y: 2}
console.log(obj2) //{x: 2, y: 2}
console.log(obj2 === obj1); // false
var obj1 = {
x: 1,
y: {
m: 1
}
};
var obj2 = Object.assign({}, obj1);
console.log(obj1) //{x: 1, y: {m: 1}}
console.log(obj2) //{x: 1, y: {m: 1}}
obj2.y.m = 2; //修改obj2.y.m
console.log(obj1) //{x: 1, y: {m: 2}}
console.log(obj2) //{x: 2, y: {m: 2}}
console.log(obj2 === obj1); // false
不难看出,虽然这个方法生成了一个新的实例,但是这个方法仅仅只拷贝了对象的第一层的属性,并没有对深层次的对象发生拷贝。
深拷贝
在堆中重新分配内存,并且把源对象所有属性都进行新建拷贝,以保证深拷贝的对象的引用图不包含任何原有对象或对象图上的任何对象,拷贝后的对象与原来的对象是完全隔离,互不影响
那么怎么实现深拷贝呢?
利用Jquery.extend方法
利用lodash的cloneDeep的方法