背景:
因为对象是复杂数据类型,复制的是对象的引用地址,实际上指向的是同一个数据,所以改变了复制的对象的值,源对象的值也会被改变。 但这往往不是我们想要的,所以要进行深拷贝
拷贝方法(深浅):
Object.assign(),Array.slice(), Array.concat()都是浅拷贝,- 也就是只有第一层的修改不会影响源对象,深层次的就会影响
JSON.parse(JSON.stringify(obj)):JSON.stringify()本意是将对象转为JSON格式,JSON格式序列化本身就不支持fn、undefined、Symbol、循环引用,所以使用 也不够完善- 手写递归拷贝 ---->👇🏻
思考过程:
- 不是对象和数组的话,直接返回本身 (可以思考下是否需要处理Date 和RegExt的情况)
- 为了避免循环引用,要传入一个hash WeakMap();
- 声明初始值的时候要区分是数组类型还是对象类型;
- 使用for in 遍历对象,但因for in遍历的包括原型链上的属性,所以要使用hasOwnProperty 过滤掉
- deepCopy() 的返回值赋值给新对象里的对应key。
forin 也可以遍历数组,for of 不能遍历对象
直接上代码:
function deepCopy(obj, hash= new WeakMap()){
let isOject = Object.prototype.toString.call(obj) == '[object Object]';
let isArray = Array.isArray(obj);
if(!isArray && !isOject){
// 不是对象也不是数组: RegExt Date fn
// function 的返回值是[object Function]
// reg 的返回值是[object RegExt]
return obj;
}
if(hash.has(obj)){
// 循环引用自身
return hash.get(obj);
}
let new_obj = Array.isArray(obj) ? new Array(): new Object();
hash.set(obj,'key');
for(let key in obj){ // in 遍历包括原型链上的属性
if(obj.hasOwnProperty(key)){ // 判断只是自身的属性,过滤掉原型链上的属性
new_obj[key] = deepCopy1(obj[key],hash);
}
}
return new_obj;
}
let obj1 = {
name: "初中",
description: "初中休学一年"
};
var person = {
name:Symbol('张小'),
age:undefined,
reg:new RegExp(/\w/),
time:new Date(),
school:{
level1:"小学",
level2: obj1,
level3:{
name:"高中",
friends:["小红","小白"],
getTeacher: function(){return 'zhang'},
}
}
}
obj1.name = person; // 互为循环引用
console.log("深拷贝对象::----", deepCopy(person));