对象相互赋值的一些关系,分别包括:
- 引入的赋值:指向同一个对象,相互之间会影响;
- 对象的浅拷贝:只是浅层的拷贝,内部引入对象时,依然会相互影响;
- 对象的深拷贝:两个对象不再有任何关系,不会相互影响;
可以通过JSON.parse来实现深拷贝,但存在以下弊端:
- 这种深拷贝的方式其实对于函数、Symbol等是无法处理的;
- 并且如果存在对象的循环引用,也会报错的;
一、简单的深拷贝函数实现
自定义深拷贝的基本功能:
二、对其他数据类型的值进行处理
包括:数组、函数、Symbol、Set、Map
function isObject(value) {const valueType = typeof valuereturn (value !== null) && (valueType === "object" || valueType === "function")
}function deepClone(originValue) {// 判断是否是一个Set类型if (originValue instanceof Set) {return new Set([...originValue])}// 判断是否是一个Map类型if (originValue instanceof Map) {return new Map([...originValue])}// 判断如果是Symbol的value, 那么创建一个新的Symbolif (typeof originValue === "symbol") {return Symbol(originValue.description)}// 判断如果是函数类型, 那么直接使用同一个函数if (typeof originValue === "function") {return originValue}// 判断传入的originValue是否是一个对象类型if (!isObject(originValue)) {return originValue}// 判断传入的对象是数组, 还是对象const newObject = Array.isArray(originValue) ? []: {}for (const key in originValue) {newObject[key] = deepClone(originValue[key])}// 上面的for循环是遍历不到key为Symbol的// 需要对key为Symbol的清空进行特殊处理const symbolKeys = Object.getOwnPropertySymbols(originValue)for (const sKey of symbolKeys) {// const newSKey = Symbol(sKey.description)newObject[sKey] = deepClone(originValue[sKey])}return newObject
}// 测试代码
let s1 = Symbol("aaa")
let s2 = Symbol("bbb")const obj = {name: "why",age: 18,friend: {name: "james",address: {city: "广州"}},// 数组类型hobbies: ["abc", "cba", "nba"],// 函数类型foo: function(m, n) {console.log("foo function")console.log("100代码逻辑")return 123},// Symbol作为key和value[s1]: "abc",s2: s2,// Set/Mapset: new Set(["aaa", "bbb", "ccc"]),map: new Map([["aaa", "abc"], ["bbb", "cba"]])
}const newObj = deepClone(obj)
console.log(newObj === obj) // falseobj.friend.name = "kobe"
obj.friend.address.city = "成都"
console.log(newObj)
console.log(newObj.s2 === obj.s2) // false
三、对循环引用进行处理
function isObject(value) {const valueType = typeof valuereturn (value !== null) && (valueType === "object" || valueType === "function")
}function deepClone(originValue, map = new WeakMap()) {// 判断是否是一个Set类型if (originValue instanceof Set) {return new Set([...originValue])}// 判断是否是一个Map类型if (originValue instanceof Map) {return new Map([...originValue])}// 判断如果是Symbol的value, 那么创建一个新的Symbolif (typeof originValue === "symbol") {return Symbol(originValue.description)}// 判断如果是函数类型, 那么直接使用同一个函数if (typeof originValue === "function") {return originValue}// 判断传入的originValue是否是一个对象类型if (!isObject(originValue)) {return originValue}// 判断当前传进来的对象是否已经在map中存在,如果是的话,// 直接返回key为这个对象的value,也就是我们所创建出来的newObject对象if (map.has(originValue)) {return map.get(originValue)}// 判断传入的对象是数组, 还是对象const newObject = Array.isArray(originValue) ? []: {}// 在第一次创建好newObject对象后,将其存去map中,key为需要进行深拷贝的对象,值为新创建好newObject对象map.set(originValue, newObject)for (const key in originValue) {newObject[key] = deepClone(originValue[key], map) // 把map作为函数的第二个参数传入}// 对Symbol的key进行特殊的处理const symbolKeys = Object.getOwnPropertySymbols(originValue)for (const sKey of symbolKeys) {// const newSKey = Symbol(sKey.description)newObject[sKey] = deepClone(originValue[sKey], map) // 把map作为函数的第二个参数传入}return newObject
}// deepClone({name: "why"})// 测试代码
let s1 = Symbol("aaa")
let s2 = Symbol("bbb")const obj = {name: "why",age: 18,friend: {name: "james",address: {city: "广州"}},// 数组类型hobbies: ["abc", "cba", "nba"],// 函数类型foo: function(m, n) {console.log("foo function")console.log("100代码逻辑")return 123},// Symbol作为key和value[s1]: "abc",s2: s2,// Set/Mapset: new Set(["aaa", "bbb", "ccc"]),map: new Map([["aaa", "abc"], ["bbb", "cba"]])
}obj.info = objconst newObj = deepClone(obj)
console.log(newObj === obj)obj.friend.name = "kobe"
obj.friend.address.city = "成都"
console.log(newObj)
console.log(newObj.s2 === obj.s2)console.log(newObj.info.info.info)