前情回顾
:::info
对象的深拷贝:在对象相互赋值时,两个对象不再有任何关系,不会互相影响
在前面我们就有使用过JSON.parse来实现深拷贝
:::
简单实现——JSON.parse
:::info
使用过JSON.parse来简单实现深拷贝的功能
:::
const obj = {
name: "why",
friend: {
name: "kobe"
},
foo: function() {
console.log("foo function")
}
}
const info = JSON.parse(JSON.stringify(obj))
console.log(info === obj)
obj.friend.name = "james"
console.log(info)
弊端:
- 这种深拷贝的方式其实对于函数、Symbol等是无法处理的;
并且如果存在对象的循环引用,也会报错的;
自定义函数对深拷贝函数简单实现
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)
弊端:
功能优化——对象中其他类型的深拷贝
:::info
数组/函数 类型/Symbol/Map/Set
:::
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) {
newObject[sKey] = deepClone(originValue[sKey]) // 在不同对象里面使用同一个key值是不会冲突的
}
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"]])
}
功能优化——循环引用
:::info
obj.info = obj 这种就叫做循环引用
问题:将 obj.info = obj 传入函数中,函数会不断地去调用,最后栈溢出报错
方案:使用WeakMap
:::
图解:
思路:
创建一个map对象,在循环引用第二次拷贝函数的时候可以拿到map对象,拿到之后判断原来有没有设置过newObject :
- 如果没有则创建新的newObject
- 如果已经有了就不再创建新的newObject而是将已有的newObject 返回
// 在参数里面创建新的map对象
// 并且当传入参数有map对象时候不会创建新的map对象,
// 可以保证 同一次循环引用时map对象唯一
function deepClone(originValue, map = new WeakMap()) {
...
if (!isObject(originValue)) {
return originValue
}
// 判断原来有没有设置过newObject
if (map.has(originValue)) {
return map.get(originValue)
}
const newObject = Array.isArray(originValue) ? []: {}
// 将传入的originValue和newObject进行映射(设置newObject)
map.set(originValue, newObject)
for (const key in originValue) {
newObject[key] = deepClone(originValue[key], map)
}
const symbolKeys = Object.getOwnPropertySymbols(originValue)
for (const sKey of symbolKeys) {
newObject[sKey] = deepClone(originValue[sKey], map)
}
return newObject
}