深浅拷贝实际上针对的是复杂数据类型而言的,浅拷贝只复制对象的一层属性,深拷贝则复制了所有层级的属性。因为Js存储对象是存地址的,所以浅拷贝会导致obj1和obj2指向同一块内存地址,而深拷贝则是为对象开辟一块新的内存,复制对象进来,因此对于深拷贝而言,obj1和obj2的操作互不影响。
指的是将一个对象的属性值复制到另外一个对象,如果有的属性的值为引用类型的话,那么会将这个引用的地址复制给对象,因此两个对象会有同一个引用类型的引用。浅拷贝可以使用Object.assign()和展开运算符来实现(如:const {a, b, c} = {1, 2, {3, 4}}; 所以c = {3, 4})。 如何实现一个对象或者数组的浅拷贝。 想一想,好像很简单,遍历对象,然后把属性和属性值都放在一个新的对象就好了~
function shallowCopy(object) {
// 只拷贝对象
if (!object || typeof object !== "object") {
return;
}
// 根据object的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {};
// 遍历object,并且判断是object的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key];
}
}
return newObject;
}
相对于浅拷贝而言,如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。深拷贝对于一些对象可以使用JSON的两个函数来实现,但是由于JSON的对象格式化比js的对象格式化更加严格,所以如果属性值里面出现函数或者Symbol类型的值时,会转换失败。 那如何实现一个深拷贝呢?说起来也简单,我们在拷贝的时候判断一下属性值的类型,如果是对象,我们递归调用深拷贝函数不就好了~
function deepCopy(object) {
if (!object || typeof object !== "object") return;
let newObject = Array.isArray(object) ? [] : {};
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
}
}
return newObject;
}
- 【性能问题】尽管使用深拷贝会完全克隆一个新对象,不会产生副作用,但是深拷贝使用了递归,性能会不如浅拷贝,在开发中,还是要根据实际情况选择。
- 【JSON对象的parse和stringify】JSON对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串, 借助这两个方法,也可以实现对象的深拷贝。
//例1
var source = { name:"source", child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
target.name = "target"; //改变target的name属性
console.log(source.name); //source
console.log(target.name); //target
target.child.name = "target child"; //改变target的child
console.log(source.child.name); //child
console.log(target.child.name); //target child
//例2
var source = { name:function(){console.log(1);}, child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
//例3
var source = { name:function(){console.log(1);}, child:new RegExp("e") }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
console.log(target.child); //Object {}
这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。 还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。