1. 首先我们需要知道关于浅拷贝与深拷贝的问题。
简单来讲,浅拷贝是指向和拷贝对象相同的内存地址,因为是共用一个地址,所以当拷贝对象发生变化时,我们新的对象也会发生变化。
let arr1 = [1, 2, 3]
let arr2 = arr1
console.log(arr2)
arr1[0] = 0
console.log(arr2)
console.log(arr1)
这里我们可以看到在进行浅拷贝的时候,我们改变被拷贝对象,相应的拷贝对象也会发生变化。
而深拷贝的时候,我们是开辟了一块新的内存地址来存放新的对象,这样两个对象是指向不同内存地址,所以对于被拷贝对象进行修改时,不会影响到拷贝的对象。
在这里对于简单数组我们可以通过解构赋值来进行深拷贝。
let arr1 = [1, 2, 3]
let arr2 = [...arr1]
console.log(arr2)
arr1[0] = 0
console.log(arr2)
console.log(arr1)
当然这里只是进行简单的复制。
2. 关于Object.assign()
Object.assign是用于对象的合并,它可以将一个或多个可枚举的对象合并到一个新的对象(注意:这里如果对象有继承的属性,则不会合并过去)
let obj = {
arr: [1, 2, 3],
name: 'AAA',
person: {
age: 0,
children: {
name: '小华'
}
}
}
let obj2 = Object.assign({}, obj)
console.log(obj)
console.log(obj2)
我们可以通过Object.assign进行对象的拷贝,但这里需要注意一点,若是简单的基础对象,Object.assign()实现的就是深拷贝,即开辟改变源对象数据,对于新的对象不会影响。
let obj = {
arr: [1, 2, 3],
name: 'AAA',
}
let obj2 = Object.assign({}, obj)
obj.name = 'BBB'
console.log(obj)
console.log(obj2)
这里我们的obj对象只是第一层键值对,所以在这里就是进行的深拷贝,我们改变obj的name但是obj2的name没有发生变化。但是对于深层次的对象来说就不一样了。
let obj = {
arr: [1, 2, 3],
name: 'AAA',
person: {
age: 0,
children: {
name: '小华'
}
}
}
let obj2 = Object.assign({}, obj)
obj.person.age = 1
console.log(obj)
console.log(obj2)
在这里我们在obj里定义了一个对象person,在我们修改person里的age的时候obj2.里person的age也发生变化,很明显这属于浅拷贝。但是我们不能就这么定义现在的拷贝是属于浅拷贝。
如果我们同时修改深层次的对象的值,以及简单的数值,
let obj = {
arr: [1, 2, 3],
name: 'AAA',
person: {
age: 0,
children: {
name: '小华'
}
}
}
let obj2 = Object.assign({}, obj)
obj.person.age = 1
obj.name = 'BBB'
console.log(obj)
console.log(obj2)
我们可以发现我们修改的obj.name,obj2里的没有发生变化,但是修改的obj.person.age,obj2里已经发生变化了,我们可以这样理解Object.assign()对于第一层数据进行了深拷贝,但是对于深层次的数据就进行了浅拷贝。
在这里需要注意一点,Object.assign()是合并对象的,如果是字符串,则会以数组形式拷贝到对象中
let str1 = 'LIMING'
let str = Object.assign({}, str1)
console.log(str)
3. 深拷贝
由于简单数据类型string,number没有深拷贝,浅拷贝的说法。在这里我们对于数组,对象进行深拷贝的时候可以自己定义相关的方法,其实原理就是进行遍历回调来赋值
function deepClone(source) {
// 对于数组与对象来说,他的本质类型都是object
//这里如果不是对象则停止
if (!source && typeof source !== 'object') {
return false
}
// 我们通过当前值的构造函数来判断是数组还是对象来初始化拷贝值
let targetObj = source.constructor === Array ? [] : {}
// for-in对于数组和对象都能进行遍历其中key是他的索引值
for (let key in source) {
// 判断当前值是否有这个索引
if (source.hasOwnProperty(key)) {
// 这里我们进行层级的判断如果当前索引值的数据仍是对象或数组我们需要进行回调来赋值
if (source[key] && typeof source[key] === 'object') {
// targetObj[key] = source[key].constructor === Array ? [] : {}
targetObj[key] = deepClone(source[key])
} else {
// 若是简单数据类型边直接赋值
targetObj[key] = source[key]
}
}
}
return targetObj
}
let obj = {
arr: [1, 2, 3],
name: 'AAA',
person: {
age: 0,
children: {
name: '小华'
}
}
}
let obj5 = deepClone(obj)
obj.person.age = 1
obj.person.children.name = '李明'
console.log(obj)
console.log(obj5)
在这里我们可以看到我们改变obj的数值,obj5不会发生任何变化,已经完成了深拷贝。
另外我们也可以将被拷贝对象转为字符串在进行转值也能完成深拷贝,但对于层级很深的可能会有一些问题。
let data = JSON.stringify(obj)
let obj6 = JSON.parse(data)
obj.person.age = 1
obj.person.children.name = '李明'
console.log(obj)
console.log(obj6)
这样也能实现深拷贝。