概述
在开始实现深拷贝之前,有必要首先了解一下引用赋值、浅拷贝和深拷贝之间的关系。他们都是复制对象时经常采用的方式,有一定的联系和区别:
-
引用赋值:将一个对象赋值给另一个变量时,实际上是将对象的引用地址赋值给了另一个变量,即两个变量指向同一块内存地址的对象。这意味着,对其中一个变量所指向的对象进行修改,另一个变量也会受到影响。
-
浅拷贝:复制对象时,只复制对象中的基本类型属性和对象引用地址,而不复制对象的子对象或数组的引用地址。这样,当对复制后的对象进行修改时,不会影响原始对象,但如果原始对象中包含的子对象或数组的引用地址发生改变,复制后的对象和原始对象都会受到影响。
-
深拷贝:复制对象时,完整复制对象中的所有属性和子属性,包括嵌套对象和数组。这样,当对复制后的对象进行修改时,不会影响原始对象和原始对象中的子对象或数组。
总之,引用赋值会使两个变量指向同一个对象,浅拷贝只复制基本类型属性和对象引用地址,而深拷贝会完整复制所有属性和子属性。因此,在不同的场景中需要根据需要选择不同的方式。
说了这么多,我们可能还是有些糊涂,没事,举个例子就懂了!
啥也不说了,看代码吧
var a = 3
// 浅拷贝
var b = a
b = 5
console.log(a, b) // 3,5
const s1 = Symbol()
const s2 = Symbol()
const info = {
name: "Machigen",
friend: {
name: "kobe"
},
foo: function() {
console.log("foo function")
},
[s1]: "abc",
s2: s2
}
// 引用赋值:更改obj或info中的任何一个,另一个就会改变
const obj = info
console.log(info === name) // true
// 浅拷贝
const obj2 = { ...info }
obj2.name = 'Messi'
obj2.friend.name = 'Taylor'
console.log(info.name, obj2.name) // Machigen, Messi
console.log(info.friend.name, obj2.friend.name) // Taylor, Taylor
// 深拷贝
const obj3 = JSON.Stringfy(info)
obj3.name = 'Messi'
obj3.friend.name = 'Taylor'
console.log(info.name, obj3.name) // Machigen, Messi
console.log(info.friend.name, obj3.info.name) // kobe, Taylor
好了,这下我们应该知道这三者的大致联系与区别了吧,接下来一起来实现 一下吧
1.基本实现
// 判断一个标识符是否是一个对象类型
function isObject(value) {
// array, object => object
// function => function => object
// null => object
// 但要将null排除在外
const valueType = typeof value
return (value !== null) && (valueType === "object" || valueType === "function")
}
function deepCopy(originValue) {
// 判断传入的originValue是否是一个对象类型
if(!isObject(originValue))
return originValue
const newObject = {}
for(let key in originValue) {
newObject[key] = deepCopy(originValue[key])
}
return newObject
}
// 测试代码
const obj = {
name: "Messi",
age: 18,
friend: {
name: "Jason",
address: {
city: "上海"
}
}
}
const newObj = deepClone(obj)
console.log(newObj === obj) // false
obj.friend.name = "kobe"
obj.friend.address.city = "北京"
console.log(newObj)
2.对各种类型进行判断
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])
}
return newObject
}
// 测试代码
let s1 = Symbol("aaa")
let s2 = Symbol("bbb")
const obj = {
name: "Machigen",
age: 18,
friend: {
name: "Lee",
address: {
city: "广州"
}
},
// 数组类型
hobbies: ["abc", "cba", "nba"],
// 函数类型
foo: function(m, n) {
console.log("foo function")
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) // false
obj.friend.name = "kobe"
obj.friend.address.city = "成都"
console.log(newObj)
console.log(newObj.s2 === obj.s2) // false