自定义深拷贝函数
一.简单的深拷贝
- 代码示例:
const s1 = Symbol()
const s2 = Symbol()
const obj = {
name: "why",
friend: {
name: "kobe"
},
foo: function() {
console.log("foo function")
},
[s1]: "abc",
s2: s2
}
obj.inner = obj
const info = JSON.parse(JSON.stringify(obj))
console.log(info === obj)
obj.friend.name = "james"
console.log(info)
二.手写深拷贝函数
-
基本实现
function isObject(value) { const valueType = typeof value 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: "why", age: 18, friend: { name: "james", address: { city: "广州" } } } const newObj = deepClone(obj) console.log(newObj === obj) obj.friend.name = "kobe" obj.friend.address.city = "成都" console.log(newObj)
-
优化深拷贝(增加新的功能)
- 用上述基本实现深拷贝的时候,数组是也会拷贝成为对象的,因此需要对其进行优化。
- 函数的话一般不做拷贝处理,但是也要拿到函数,上述基本实现会把传入的函数变成{}显然是不合理的
- 增加一下对于set和map的处理
function isObject(value) { const valueType = typeof value return (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, 那么创建一个新的Symbol if (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]) } // 对Symbol的key进行特殊的处理 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/Map set: new Set(["aaa", "bbb", "ccc"]), map: new Map([["aaa", "abc"], ["bbb", "cba"]]) } const newObj = deepClone(obj) console.log(newObj === obj) obj.friend.name = "kobe" obj.friend.address.city = "成都" console.log(newObj) console.log(newObj.s2 === obj.s2)
-
解决循环引用问题
-
起因加入上述功具函数的测试代码obj对象有一个属性执行自己:obj.info = obj
-
造成的后果就是在调用的时候陷入递归的循环引用
- deepClone方法只有在满足一定条件之后才return,如果传入的是对象,他会继续走下面的遍历
- newObject[info ] = deepClone(originValue[info ])----------->originValue[info ]是对象 继续赋值
- newObject[info ] = deepClone(originValue[info ])------------>originValue[info ]还是对象…
- …以此反复,陷入死循环
for (const key in originValue) { newObject[key] = deepClone(originValue[key]) }
- deepClone方法只有在满足一定条件之后才return,如果传入的是对象,他会继续走下面的遍历
-
代码:
function isObject(value) { const valueType = typeof value return (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, 那么创建一个新的Symbol if (typeof originValue === "symbol") { return Symbol(originValue.description) } // 判断如果是函数类型, 那么直接使用同一个函数 if (typeof originValue === "function") { return originValue } // 判断传入的originValue是否是一个对象类型 if (!isObject(originValue)) { return originValue } if (map.has(originValue)) { return map.get(originValue) } // 判断传入的对象是数组, 还是对象 const newObject = Array.isArray(originValue) ? []: {} map.set(originValue, newObject) for (const key in originValue) { newObject[key] = deepClone(originValue[key], map) } // 对Symbol的key进行特殊的处理 const symbolKeys = Object.getOwnPropertySymbols(originValue) for (const sKey of symbolKeys) { // const newSKey = Symbol(sKey.description) newObject[sKey] = deepClone(originValue[sKey], 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/Map set: new Set(["aaa", "bbb", "ccc"]), map: new Map([["aaa", "abc"], ["bbb", "cba"]]) } obj.info = obj const 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)
-