学习深拷贝和浅拷贝时看了某站一个阿婆主的讲解,个人觉得不错哦!(【前端面试题之深拷贝与浅拷贝-哔哩哔哩】https://b23.tv/6WeEfop)
首先我们需要知道js的数据类型:
1.基本数据类型(六种)存放在栈中:Number、String、Boolean、Null、Undefined、Symbol(ES6),这些数据可以直接保存在变量中的实际值。(按值传递)
var a = 25;
var b = a;
a=11;
console.log("a=", a);
console.log("b=", b);
在修改a时并不会改到b;
2.引用数据类型(也叫对象数据类型,同时保存在栈区和堆区中):function、object、array、date、RegExp。
var obj1 = {a:10,b:20,c:30};
var obj2 = obj1;
obj2.b = 100;
console.log(obj1);
console.log(obj2);
复制一份obj1叫做obj2,然后把obj2.b改成100,但却不小心改到obj1.b,因为他们根本是同一个对象,这就是所谓的浅拷贝。
正确写法:
var obj1 = {a:10,b:20,c:30};
var obj2 = {a:obj1.a,b:obj1.b,c:obj1.c};
obj2.b = 100;
console.log(obj1);
console.log(obj2);
这样就是深拷贝,不会改到原本的obj1.
实现深拷贝的方法:
1.JSON.parse(JSON.stringfy())
缺点:
(1)函数无法拷贝
(2)正则无法拷贝
(3)undefined无法拷贝
优点:二层以下也可以实现深拷贝
let obj1 = {
a:1,
say(){
console.log(1)
},
reg:/\d/,
date:undefined,
person:{
name :'张三'
}
}
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.a = 2;
obj1.person.name = "李四";
console.log("obj2=", obj2)
2.Object.assign(obj2,obj1)
优点:
(1)可以拷贝函数
(2)可以拷贝正则
(3)可以拷贝undefined
缺点:二层以下不能实现深拷贝(所以该方法实现的是浅拷贝)
let obj1 = {
a:1,
say(){
console.log(1)
},
reg:/\d/,
date:undefined,
person:{
name :'张三'
}
}
let obj2 = {};
Object.assign(obj2,obj1);
obj1.a = 2;
obj1.person.name = "李四";
所以我们应该怎么实现深度拷贝呢?
(下面这部分内容是转了一位大佬的博客内容:【JS】深拷贝与浅拷贝的区别,实现深拷贝的几种方法 - 听风是风 - 博客园)
(1)递归去复制所有层级属性
function deepClone(obj) { let objClone = Array.isArray(obj) ? [] : {} if (obj && typeof obj === 'object') { for (let key in obj) { if (obj.hasOwnProperty(key)) { // 判断obj子元素是否为对象,如果是,递归复制 if (obj[key] && typeof obj[key] === 'object') { objClone[key] = deepClone(obj[key]) } else { objClone[key] = obj[key] } } } } return objClone } let a = [1,2,3,4] let b = deepClone(a) // [1,2,3,4] a[0] = 2 // [2,2,3,4] b // [1,2,3,4]
那是不是说slice方法也是深拷贝了,毕竟b也没受a的影响,上面说了,深拷贝是会拷贝所有曾经的属性,还是这个例子,我们把a改改
let a = [0,1,[2,3],4] let b = a.slice() a[0] = 1 a[2][0] = 1 // [1,1,[1,3],4] b // [0,1,[1,3],4]
拷贝的不彻底啊,b对象的一级属性确实不受影响了,但是二级属性还是没能拷贝成功,仍然脱离不了a的控制,说明slice根本不是真正的深拷贝。
这里引用知乎问答里面的一张图
第一层的属性确实深拷贝,拥有了独立的内存,但更深的属性却仍然公用了地址,所以才会造成上面的问题。
同理,concat方法与slice也存在这样的情况,他们都不是真正的深拷贝,这里需要注意。
(2)用JSON对象的parse和stringify
function deepClone (obj) { let _obj = JSON.stringify(obj) let objClone = JSON.parse(_obj) return objClone } let a = [0,1,[2,3],4] let b = deepClone(a) a[0] = 1 a[2][0] = 1 // [1,1,[1,3],4] b // [0,1,[2,3],4]
(3)JQ的extend方法
JQ里有extend方法也可以拷贝对象。
$.extend([deep ], target, object1 [, objectN ])
deep表示是否深拷贝,为true为深拷贝;为false,为浅拷贝。
target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。
let a = [0,1,[2,3],4] let b = $.extend(true, [], a) a[0] = 1 a[2][0] = 1 // [1,1,[1,3],4] b // [0,1,[2,3],4]