1.自定义深拷贝函数
-
前面我们已经学习了对象相互赋值的一些关系,分别包括:
- 引入的赋值:指向同一个对象,相互之间会影响。
- 对象的浅拷贝:只是浅层的拷贝,内部引入对象时,依然会相互影响。
- 对象的深拷贝:两个对象不再有任何关系,不会相互影响。
-
前面我们已经可以通过一种方法来实现深拷贝:JSON.pare
- 这种深拷贝对函数、Symbol是无法处理的,它会忽略掉
- 并且如果存在循环引用,也会报错。
-
自定义深拷贝函数
- 自定义深拷贝的基本功能
- 对Symbol的key进行处理
- 对其他数据类型的值进行处理:数组、函数、Symbol、Set、Map
- 对循环引用的处理
const s1=Symbol()
const s2=Symbol()
const obj={
name:"wjy",
age:20,
friend:{
name:"kobe"
},
foo:function(){
console.log("foo function");
},
[s1]:"abc",
s2:s2
}
// obj.inner=obj;// * TypeError: Converting circular structure to JSON
// * JSON拷贝存在弊端:不能对函数、Symbol转换,不能循环引用
const newObj=JSON.parse(JSON.stringify(obj));
console.log(newObj==obj);
console.log(newObj);
2. 实现浅拷贝
function deepClone(originValue){
const newObject={};
for(const key in originValue){
newObject[key]=originValue[key]
}
return newObject
}
// 测试代码
const obj={
name:"wjy",
age:18,
friend:{
name:"hyz"
}
}
const newObj=deepClone(obj);
console.log(newObj==obj);
newObj.friend.name="kobe";
console.log(obj.friend.name);
/**
* 实现的是浅拷贝
*/
3.实现深拷贝的过程
3.1 深拷贝的基本实现
function isObject(value){
// * 判断是否是一个对象
const valueType=typeof value;
// * 注意:null也是一个对象
return (value!==null)&&(valueType=="object" || valueType=="function")
}
function deepClone(originValue){
// 判断传入的originValue 是否是一个对象
// * 如果不是一个对象,直接将值返回
if(!isObject(originValue)) return originValue;
const newObject={};
for(const key in originValue){
newObject[key]=deepClone(originValue[key])
}
return newObject;
}
// 测试代码
const obj={
name:"wjy",
age:18,
friend:{
name:"hyz",
hobbies:["打篮球","看书"]
}
}
const newObj=deepClone(obj);
console.log(newObj==obj);
newObj.friend.name="kobe";
console.log(obj.friend==newObj.friend);
/**
* 基本的深拷贝
*/
3.2 其他类型的处理
- Symbol类型
- 对象的Symbol属性处理
- Set
- Map
- function
- Array
function isObject(value){
// * 判断是否是一个对象
const valueType=typeof value;
// * 注意:null也是一个对象
return (value!==null)&&(valueType=="object" || valueType=="function")
}
function deepClone(originValue){
// * 1. 判断是否是一个Set类型
if(originValue instanceof Set){
let set=new Set();
originValue.forEach(key=>{
set.add(deepClone(key));
})
return set;
}
// * 2. 判断是否是一个Map类型
if(originValue instanceof Map){
let map=new Map();
originValue.forEach((key,value)=>{
originValue.set(deepClone(key),deepClone(value))
})
return map;
}
// *3. 判断如果是Symbol的value,那么创建一个新的Symbol
if(typeof originValue=="symbol") return Symbol(originValue.description);
// * 4..判断如果是函数类型,那么直接使用同一个函数
if(typeof originValue=="function") return originValue;
// 判断传入的originValue 是否是一个对象
// * 5. 如果不是一个对象,直接将值返回
if(!isObject(originValue)) return originValue;
// * 6.判断对象是否为数组还是对象,如果为数组,初始为[],否则为{}
const newObject=Array.isArray(originValue)?[]:{};
for(const key in originValue){
newObject[key]=deepClone(originValue[key])
}
// * 对Symbol的key进行特殊处理
const symbolKeys=Object.getOwnPropertySymbols(originValue);
for(const sKey of symbolKeys){
newObject[sKey]=deepClone(originValue[sKey])
}
return newObject;
}
// 测试代码
const s1=Symbol();
const s2=Symbol();
const obj={
name:"wjy",
age:18,
friend:{
name:"hyz",
hobbies:["打篮球","看书"]
},
[s1]:"abc",
s2:s2,
set:new Set(["aaa","bbb","ccc"]),
map:new Map([["aaa","abc"],["bbb","cba"]])
}
const newObj=deepClone(obj);
console.log(newObj==obj);
newObj.friend.name="kobe";
console.log(obj.friend==newObj.friend);
console.log(newObj);
/**
* * 1.for...of不能遍历 Symbol属性
* * 解决办法:1.使用Object.getOwnPropertySymbols,进行遍历
* * 2. 对set类型进行拷贝
*/
3.3 循环引用的实现
- 将原对象作为key,新对象作为value,存入到WeakMap
- 如果遇到已经存储过,从WeakMap取出来并返回出去
- 如果没有存储,而且是一个对象,则存入到weakMap中。
function isObject(value){
// * 判断是否是一个对象
const valueType=typeof value;
// * 注意:null也是一个对象
return (value!==null)&&(valueType=="object" || valueType=="function")
}
function deepClone(originValue,weakMap=new WeakMap()){
// * 1. 如果不是一个对象,直接将值返回
if(!isObject(originValue)) return originValue;
// * 2.如果是以前已经存在的,直接返回对应的引用
if(weakMap.get(originValue)) return weakMap.get(originValue);
// * 3. 判断是否是一个Set类型
if(originValue instanceof Set){
let set=new Set();
weakMap.set(originValue,set);
originValue.forEach(key=>{
set.add(deepClone(key,weakMap));
})
return set;
}
// * 4. 判断是否是一个Map类型
if(originValue instanceof Map){
let map=new Map();
weakMap.set(originValue,map);
originValue.forEach((value,key)=>{
map.set(deepClone(key,weakMap),deepClone(value,weakMap))
})
return map;
}
// * 5.判断如果是函数类型,那么直接使用同一个函数
if(typeof originValue=="function") return originValue;
// * 7.判断对象是否为数组还是对象,如果为数组,初始为[],否则为{}
const newObject=Array.isArray(originValue)?[]:{};
weakMap.set(originValue,newObject);
for(const key in originValue){
newObject[key]=deepClone(originValue[key],weakMap)
}
// * 对Symbol的key进行特殊处理
const symbolKeys=Object.getOwnPropertySymbols(originValue);
for(const sKey of symbolKeys){
newObject[sKey]=deepClone(originValue[sKey],weakMap)
}
return newObject;
}
// 测试代码
const s1=Symbol();
const s2=Symbol();
const obj={
name:"wjy",
age:18,
friend:{
name:"hyz",
hobbies:["打篮球","看书"]
},
[s1]:"abc",
s2:s2,
set:new Set(["aaa","bbb","ccc"]),
map:new Map([["aaa","abc"],["bbb","cba"],["ccc",{name:"wjy"}]])
}
console.log("obj",obj);
obj.info=obj;
const newObj=deepClone(obj);
console.log(newObj==obj);
newObj.friend.name="kobe";
console.log(obj.friend==newObj.friend);
console.log(newObj);
/**
*
*/