今天来讨论一下javascript中的浅拷贝和深拷贝。
首先我们先来看一下两个问题:
1.什么叫浅拷贝和深拷贝?
浅拷贝:将原对象的自身属性复制给新对象,如果原对象的属性是基本类型则直接进行值赋值,如果是引用类型则进行引用赋值,也就是说只进行一层赋值。
深拷贝:将原对象的自身属性复制给新对象,如果原对象的属性是基本类型则直接进行值赋值,如果是引用类型则复制这个引用类型,使得目标对象拥有一个引用类型且和这个源属性一模一样,而非是一个指针。
2.什么情况下浅拷贝、深拷贝?
由上面对两种拷贝的理解,很容易看出来,只有在对象里嵌套对象的情况下,才会根据需求讨论,我们要深拷贝还是浅拷贝。
接下来我们先了解一下js的基本类型和引用类型,有助于我们理解浅拷贝和深拷贝。
1.基本类型的变量和值是存在栈区的,如var a=1;var a = 2;如下图:
image.png
基本类型在赋值操作后,互相不影响。举个🌰
var a = 1;
var b = a;
a++;
console.log(a);//2
console.log(b);//1
2.引用数据类型:名存在栈内存,值存在堆内存,栈内存会提供一个指针指向堆内存中的值。如下图
var a = {};var b={}
image.png
所以赋值后,两个变量都保存在同一个对象地址,则两个变量指向同一个对象,所以改变任何一个变量都会受到影响。
举个🌰:
var a = {name:'li'};
var b = a;
console.log(a.name);//li
console.log(b.name);//li
a.name = 'zhang';
console.log(a.name);//zhang
console.log(b.name);//zhang
image.png
当a.name改变了,由于a与b指向的是同一个地址,所以自然b也受了影响。(另外引用赋值不是浅拷贝!! 引用赋值仅仅只是赋值个指针,两个变量都指向同一堆内存,而浅拷贝是使两个变量分别指向不同的堆内存)。
image.png
深拷贝和浅拷贝的区别:浅拷贝只是复制对象的第一层属性,而深拷贝可以对对象的属性进行递归复制。
浅拷贝:
1.for...in (只复制了对象的第一层,而对象内的对象是不同的引用,指向同一个内存空间,新的对象嵌套的对象改变,新的对象嵌套的对象也会改变)
var a = {name:"li",hobby:["eat","song","newthings"]};
var b = {};
for(var val in a){
b[val] = a[val];
}
console.log(b);// {name:"li",hobby:["zhang","song","newthings"]};
b.hobby[0] = "zhang";
b.name = "chen";
console.log(a); //{name:"li",hobby:["zhang","song","newthings"]};
console.log(b); //{name:"chen",hobby:["zhang","song","newthings"]};
2.数组 slice和concat
3.es6 扩展运算符...
深拷贝:
1.JSON.parse (JSON 并不支持函数类型的数据,所以对象中如果有函数类型不支持)
var obj= {c:1,d:[1,2,3,4,5]}
var objNew = JSON.parse(JSON.stringify(obj));
console.log(objNew);//{c:1,d:[1,2,3,4,5]}
objNew.d.push(6);
console.log(obj);//{c:1,d:[1,2,3,4,5]}
console.log(objNew);//{c:1,d:[1,2,3,4,5,6]}
2.递归拷贝
function deepClone(obj){
//=>过滤特殊情况
if(obj===null) return null;
if(typeof obj !=='object') return obj;
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Date) return new Date(obj);
//判断是数组还是对象,不直接创建空对象目的:克隆的结果和之前保持相同的所属类
let newObj = Array.isArray(obj)?[]:new obj.constructor;
for(let key in obj){
//如果是原型上的属性,不拷贝
if(obj.hasOwnProperty(key)){
newObj[key]=deepClone(obj[key]);
}
}
return newObj;
}
//对象
var obj= {c:1,d:[1,2,3,4,5]};
var obj2 = deepClone(obj);
console.log(obj2);////{c:1,d:[1,2,3,4,5]}
obj.d.push(6);
console.log(obj);//{c:1,d:[1,2,3,4,5,6]}
console.log(obj2);//{c:1,d:[1,2,3,4,5]}
var arr = [1,2,3,[1,2,4]];
//数组
var newArr = deepClone(arr);
newArr[3].push(5);
console.log(arr);//[1,2,3,[1,2,4]];
console.log(newArr);//[1,2,3,[1,2,4,5]];
这里说一下Object.assign()
,查阅了不同的资料,有把它归为浅拷贝的,也有把它归为深拷贝的,最开始也一脸懵,这里只能说如果拷贝的对象是非嵌套对象,可以说是深拷贝,如果对象中出现嵌套情况,那么它对被嵌套对象的行为就成了普通的浅拷贝了。也就是深拷贝一级对象,后面嵌套的属于浅拷贝,感觉和for..in一样。。。
3.jquery里的extend方法
$.extend( [deep ], {}, a, b )
deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
{}:将合并结果保存到新对象,这样原对象将不会发生改变
a:第一个合并的对象
b:第二个合并的对象
网友评论