深拷贝与浅拷贝
程序员文章站
2022-05-28 14:30:04
...
深拷贝和浅拷贝主要是针对引用类型的值的赋值(或复制)操作。
一、区分基本类型和引用类型
基本类型的值 | 引用类型的值 |
Undefined,Null,Boolean,String,Number | Object,Array,Date,RegExp,Function |
按值访问(可以操作保存在变量中的实际的值) | 按引用访问(操作对象时,实际上是操作对象的引用) |
保存在变量中的实际的值 | 保存在内存中的对象(不允许直接访问) |
赋值时,前后两个变量时独立的 | 赋值时,前后两个变量指向内存中的同一位置(引用类型的实际值存放在堆中) |
对于向函数传参,都是按值传递(但是引用类型的传参有点特别,如果不是重写,那么参数和外面的变量都是指向同一个堆位置,如果重写了,那参数就指向一个新创建的局部变量,原对象无影响,这说明不是按引用传递) |
检测类型的方法
1、使用typeof,可区分出string,number,boolean,undefined,object,function等
2、使用instanceof :obj instanceof Object,如果是引用类型则返回 true,否则返回false。
二、基本类型的赋值(或复制)
例一:
var num1 = 1;
var num2 = num1; //num2 = 1
num2 = 2; //num2 = 2,num1 = 1
复制前的变量对象
|
|
|
|
Num1 |
1(Number类型) |
复制后的变量对象
|
|
Num2 |
1(Number类型) |
Num1 |
1(Number类型) |
基本类型的赋值操作后的两个变量,是独立的,改变num2的值,不会影响到num1。
三、引用类型的赋值(或复制)
例二:
var obj1 = {
name : 'zsx',
age : 10
}
var obj2 = obj1; //obj1 = {name: "zsx", age: 10},obj2 = {name: "zsx", age: 10}
obj2.name = 'changeName'; //obj1 = {name: "changeName", age: 10},obj2 = {name: "changeName", age: 10}
复制前的变量对象
|
|
|
|
Obj1 |
堆地址1:指向存储在堆中的一个对象(Object类型) |
复制后的变量对象
|
|
Obj2 |
堆地址1:指向存储在堆中的一个对象(Object类型) |
Obj1 |
堆地址1:指向存储在堆中的一个对象(Object类型) |
堆内存
引用类型的赋值操作,其实复制的是指针,相同的指针使得前后两个变量指向的对象一致,因此改变obj2,会反映到obj1。
例三:
var arr1 = [1,2,3];
var arr2 = arr1; //arr1 = [1, 2, 3],arr2 = [1, 2, 3]
arr2[0] = 0; //arr1 = [0, 2, 3],arr2 = [0, 2, 3]
例四:
var array=[];
var item={};
for(var i=0;i<5;i++){
item.a=i; //在这里,修改的都是堆中同一个位置的对象,因此array中的元素都指向同一个item
array.push(item);
}
console.log(array); // [ { a: 4 }, { a: 4 }, { a: 4 }, { a: 4 }, { a: 4 } ]
for(var i=0;i<5;i++){
var item={}; //在这里,每次都新建一个对象,array中的元素指向五个不同的item
item.a=i;
array.push(item);
} //[ { a: 0 }, { a: 1 }, { a: 2 }, { a: 3 }, { a: 4 } ]
四、引用类型的浅拷贝
//浅拷贝实现
function shallowCopy (obj) {
var newObj = {};
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = obj[key]; //复制obj拥有的直接属性
}
}
return newObj;
}
例五:
var arr1 = [1,{a:2},[3,4]];
var arr2 = arr1.slice(0); //arr1 = [1,{a:2},[3,4]],arr2 = [1,{a:2},[3,4]]
arr2[0] = 0; //arr1 = [1,{a:2},[3,4]],arr2 = [0, {a:2}, Array(2)],浅拷贝
arr2[1].a = 22; //arr1 = [1,{a:22},[3,4]],arr2 = [1,{a:22},[3,4]],元素中的对象是赋值操作
arr2[2][0] = 33; //arr1 = [1,{a:2},[33,4]],arr2 = [1,{a:2},[33,4]] ,元素中的数组是赋值操作
例六:
var arr1 = [1,{a:2},[3,4]];
var arr2 = arr1.concat(); //arr1 = [1,{a:2},[3,4]],arr2 = [1,{a:2},[3,4]]
arr2[0] = 0; //arr1 = [1,{a:2},[3,4]],arr2 = [0, {a:2}, Array(2)],浅拷贝
arr2[1].a = 22; //arr1 = [1,{a:22},[3,4]],arr2 = [1,{a:22},[3,4]],元素中的对象是赋值操作
arr2[2][0] = 33; //arr1 = [1,{a:2},[33,4]],arr2 = [1,{a:2},[33,4]] ,元素中的数组是赋值操作
例七:
var obj1 = {
name : 'zsx',
friend : {
name : 'xiaohong',
age : 12
}
}
var obj2 = Object.assign({}, obj1); //obj1 = {name: "zsx", friend: {name: "zsx", age: 12}},obj2 = {name: "zsx", friend: {name: "zsx", age: 12}}
obj2.friend.name = 'changeName'; //obj1 = {name: "zsx", friend: {name: "changeName", age: 12}},obj2 = {name: "zsx", friend: {name: "changeName", age: 12}},浅拷贝
无论是利用slice、concat和Object.assign(),都是浅拷贝,子对象(子数组)是赋值操作。
五、引用类型的深拷贝
例八:利用递归进行深拷贝
//简单版深拷贝函数
function deepCopy (obj) {
//如果需要拷贝的obj是null,不是数组或者对象,则直接返回obj
if (obj === null || typeof obj !== 'object') {
return obj
}
var copy = Array.isArray(obj) ? [] : {}; //判断是对象还是数组
//通过Object.keys(obj)的方法,从obj中获取一个key数组
//注意:Object.keys(obj)不遍历原型链
Object.keys(obj).forEach(key => {
copy[key] = deepCopy(obj[key]);
});
return copy;
}
var obj1 = {
name : 'zsx',
friend : {
name : 'xiaohong',
age : 12
}
}
var obj2 = deepCopy(obj1); //obj1 = {name: "zsx", friend: {name: "xiaohong", age: 12}},obj2 = {name: "zsx", friend: {name: "xiaohong", age: 12}}
obj2.friend.name = 'changeName'; //obj1 = {name: "zsx", friend: {name: "xiaohong", age: 12}},obj2 = {name: "zsx", friend: {name: "changeName", age: 12}}
例九:利用JSON.stringify()和JSON.parse()进行深拷贝
var obj1 = {
name : 'zsx',
friend : {
name : 'xiaohong',
age : 12
}
}
//利用JSON.stringify()方法将obj1转换为字符串,将字符串赋值给obj2
var obj2 = JSON.stringify(obj1);
//再利用JSON.parse()方法解析json,重新分配空间给obj2
obj2 = JSON.parse(obj2);
obj2.friend.name = 'changeName'; //obj1 = {name: "zsx", friend: {name: "xiaohong", age: 12}},obj2 = {name: "zsx", friend: {name: "changeName", age: 12}}
六、区分赋值(=)、浅拷贝和深拷贝
赋值(=):只赋值引用类型整体的地址。
浅拷贝:复制对象(数组)的直接元素,子对象(子数组)进行赋值操作。
深拷贝:复制对象(数组)中的属性(元素),包括子对象(子数组)。
下一篇: 深拷贝与浅拷贝