深拷贝和浅拷贝是在面试中经常遇到的问题。今天在这里总结一下。
深拷贝与浅拷贝的问题,涉及到JavaScript的变量类型,先来说说变量的类型,变量类型包括基本类型和引用类型。
- 基本类型:Undefined,Null,Boolean,String,Number
- 引用类型:Array,Object,Date,RegExp,Function等
基本类型的变量直接按值存放在栈区里面,可以直接访问,比如我们平时把字符串、数字的值赋值给新变量,就是把值完全赋值过去,新变量的改变不会影响旧变量。
引用类型是存放在堆区的对象,变量在栈区中保存的是一个指针地址,指向真正保存在堆区中的对象。
浅拷贝
1.定义:浅复制只复制一层对象的属性。
- 针对对象中的基本类型是新开辟一块内存进行存储,改变新对象中的基本类型值不会影响原对象。
- 而对于对象中的引用类型,也会新开辟一块内存,不过这块内存,仅仅是用来存储对象中的引用类型的地址(即指针,这个指针还是指向存储在堆内存中的原对象)
先来看一段代码:下面这段代码只是浅复制
var obj = {
a:1,
arr: [2,3]
};
var shallowObj = shallowCopy(obj);
function shallowCopy(src){
var dst = {};
for(var prop in src){
if(src.hasOwnProperty(prop)){
dst[prop] = src[prop];
}
}
return dst;
}
console.log(shallowObj.a);//1
shallowObj.a = 3;
console.log(shallowObj.a);//3
console.log(obj.a);//1
console.log(shallowObj.arr);//[2,3]
shallowObj.arr[0] = 3;
console.log(shallowObj.arr);//[3,3]
console.log(obj.arr);//[3,3]
2.实现方式
- 可以通过es6新增的Object.assign来复制对象
let obj = {
name: "xiaoming",
hobby: [
"pinpong",
"soccer"
]
};
let copyObj = Object.assign({},obj);
copyObj.name = "xiaohong",
copyObj.hobby[0] = "jianzi";
console.log(obj);
console.log(copyObj);
- jQuery中的$.extend(,obj);Array.prototype.slice()和Array.prototype.concat()都会返回一个数组或者对象的浅拷贝。
深拷贝
1.定义:深拷贝不仅会将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上,这就不会存在上图中的obj和shallowObj的arr属性指向同一个对象的问题。
function cloneDeep(obj){
if(typeof obj !== 'object' || Object.keys(obj).length === 0 ){
return obj;
}
let resultData = {};
return recurison(obj, resultData);
}
function recurison(obj, data = {}){
for(key in obj){
if(typeof obj[key] == 'object' && Object.keys(obj[key].length > 0 )){
data[key] = recurison(obj[key]);
}else{
data[key] = obj[key];
}
}
return data;
}
var o1 = {
arr: [1, 2, 3],
obj: {
key: 'value'
},
func: function(){
return 1;
}
};
var o3 = cloneDeep(o1);
console.log(o3 === o1);//false
console.log(o3.obj === 01.obj);//false
console.log(o3.func === o1.func);//true
深拷贝对基本类型和引用类型都会新开辟一块内存来存储它们,需要注意的是,如果对象比较大,层级比较多,深复制会带来性能上的问题。所以在遇到深复制的情况时,可以考虑有没有其他替代的方案。在实际的应用场景中,也是浅复制更为常用。
2.JSON对象中的parse和stringify,JSON对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以将JSON字符串反序列化为一个js对象,通过这两个方法,也可以实现对对象的深复制。