浅拷贝与深拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会修改原对象。
赋值和浅拷贝的区别
当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。_____
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝,他们引用的是同一个地址的数据,拷贝的时候并没有给b数组创造独立的内存,只是把a数组指向数据的指针拷贝给了b;
而深拷贝就与其相反,将会给b数组创造独立的内存,并且将a数组的内容一一拷贝进来,两者互不影响。
只要进行了深拷贝,它们老死不相往来,谁也不会影响谁。
如果属性是基本类型,拷贝的是基本类型的值;
如果属性是内存地址(引用类型),拷贝的是内存地址
浅拷贝的实现
Object.assign()
Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。当object只有一层的时候,是深拷贝
Array.prototype.concat()
修改新对象会改到源对象
深拷贝的实现
一:层级拷贝,用递归实现,每一层都重新创建对象并赋值;
// 实现深拷贝function
deepClone(source) {
// 判断复制的目标是数组还是对象
const targetObj = source.constructor === Array ? [] : {};
for (const key in source) {
if (source.hasOwnProperty(key)) {
// 如果值是对象就递归
if(source[key] && typeof source[key] === 'object'){
targetObj[key] = source[key].constructor === 'Array' ? [] : {};
deepClone(targetObj[key]);
} else {
// 如果不是就直接赋值
targetObj[key] = source[key];
}
}
}
return targetObj;
}
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = deepClone(originObj);
console.log(cloneObj === originObj);
// false
cloneObj.a = 'aa';
cloneObj.c = [1,1,1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj);
// {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
console.log(originObj);
// {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const originObj2 = {
name:'张振明',
sayHello:function(){
console.log('Hello World');
}
}
console.log(originObj2);
// {name: "张振明", sayHello: ƒ}
const cloneObj2 = deepClone(originObj2);
console.log(cloneObj2);
// {name: "张振明", sayHello: ƒ}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
二:利用 JSON 对象中的 parse 和 stringify
JSON.stringify的作用是将一个javascript值转换成json字符串
JSON.parse的作用是将一个JSON字符串转换成javascript值或对象
const originArray = [1,2,3,4,5];
const cloneArray = JSON.parse(JSON.stringify(originArray));
console.log(cloneArray === originArray);
// false
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj === originObj);
// false
cloneObj.a = 'aa';
cloneObj.c = [1,1,1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj);
// {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
console.log(originObj);
// {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
JavaScript中的拷贝方法
JavaScript数组中有两个方法,concat和slice,他们都不会改变原数组,而是返回一个新数组
所以他们是可以实现对原数组的拷贝的,另外es6新增的Object.assgn 方法和 ... 展开运算符也能实现对对象的拷贝
这里只说明结论,不解释详细过程
concat
该方法可以连接两个或者更多的数组,但是它不会修改已存在的数组,而是返回一个新数组。
结论:concat 只是对数组的第一层进行深拷贝。
slice
结论:slice 只是对数组的第一层进行深拷贝。
Object.assign()
结论:Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值
… 展开运算符
结论:... 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。
总结
- 赋值运算符
=实现的是浅拷贝,只拷贝对象的引用值; - JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;
JSON.stringify实现的是深拷贝,但是对目标对象有要求;- 若想真正意义上的深拷贝,请递归。