深浅拷贝问题其实就是基本类型和引用类型数据拷贝的问题。因为基本类型的数据大小是固定的,所以他保存在栈内存中;而引用类型的数据大小不固定,因而保存在堆内存中,单引用类型在栈内存中只保存一个指向堆内存的指针。
浅拷贝:对于浅拷贝来说,如果拷贝基本类型,那么就等于赋值一样,会直接拷贝其本身;但如果拷贝的是引用类型,就只会拷贝一层,如果 原对象发生改变,那么拷贝对象也会发生改变。
深拷贝:深拷贝的话就会拷贝多层,嵌套的对象也会被拷贝出来,相当于开辟一个新的内存地址用于存放拷贝的对象。
深拷贝的实现:
第一种:ES6的Object.assign()方法
针对于对象只有一层的,不能实现真正的深拷贝。对象有多层的不能使用此方法。
Object.assign({},obj1)的意思是先建立一个空对象{},接着把obj1
中所有的属性复制过去,所以obj2
会长得跟obj1
一样,这时候再修改obj2.b
也不会影响obj1。
var obj1 = {name:'lily'};
var obj2 = Object.assign({},obj1);
obj2.name = 'bob';
console.log(obj1.name); //lily
console.log(obj2.name); //bob
第二种:将对象转成字符串再转换回来
用JSON.stringify
把对象转成字符串,再用JSON.parse
把字符串转成新的对象。
var obj = {
a:'hello',
b:{
name:'lily',
age:21
},
c:[1,2,3]
};
var obj1 = JSON.parse(JSON.stringify(obj));
obj1.a = 'world';
obj1.b = {
name:'bob',
age:15
};
obj1.c = [6,7,8];
console.log(obj.a);
console.log(obj.b);
console.log(obj.c);
console.log(obj1.a);
console.log(obj1.b);
console.log(obj1.c);
此时,即使改变了obj1里面每个属性的值,obj也不会受影响。但这种方法不能针对函数function,无效。
第三种:递归方法实现深拷贝
知识点:
1:三元表达式:
表达式 (expr1) ? (expr2) : (expr3)
在 expr1 求值为 TRUE 时的值为 expr2,在 expr1 求值为 FALSE 时的值为 expr3。
2:hasOwnProperty():用来判断一个对象是否有自己的属性
function deepClone(obj){
//先制造一个新的数组或对象,指向一个新的空间
var newObj = Array.isArray(obj) ? [] : {};
//判断obj的类型
//基本类型
if(typeof obj != 'object'){ //是普通类型的话就直接赋值,但不能直接返回obj,那样是浅拷贝
return newObj = obj;
}
//引用类型
if(obj instanceof Array){ //是数组
for(var i=0;i<obj.length;i++){
newObj[i] = obj[i];
if(typeof newObj[i] == 'object'){ //如果数组的值有对象的话,就继续递归
deepClone(newObj[i]);
}
}
}else{ //是对象
for(var key in obj){
if(obj.hasOwnProperty(key)){
if(typeof obj[key] == 'object'){ //对象中的数组和对象
newObj[key] = deepClone(obj[key]); //继续递归
}else{//对象中没有引用类型
newObj[key] = obj[key];
}
}
}
}
return newObj;
}
var obj = {
a:'haha',
b:[1,2,3],
c:function(){
console.log('hello');
}
};
var cloneObj = deepClone(obj);
cloneObj.a = '你好';
cloneObj.b = [6,7,8];
cloneObj.c = function(){
console.log('world');
};
console.log(obj);
console.log(cloneObj);