目录
拷贝分为深拷贝和浅拷贝
浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存。
深拷贝:创建一模一样的对象,不共享内存,修改新对象,旧对象保持不变。
1.尝试拷贝
- 尝试拷贝:下面两种尝试拷贝方式其实都是以赋值方式模拟拷贝,并不属于拷贝(这里是方便分类,不是官方文档名词)。
1.1.直接赋值尝试拷贝
const obj = {
name: 'Tom',
age: 18
}
const newObj = obj
console.log(newObj.name); // Tom
obj.name = 'Jack'
console.log(obj.name); // Jack
// newObj会随着obj的改变而改变
console.log(newObj.name); // Jack
可以看出直接赋值,新对象会随着原对象的改变而改变
1.2.展开运算符尝试拷贝
const obj = {
name: 'Tom',
age: 18
}
const newObj = { ...obj }
console.log(newObj.name); // Tom
obj.name = 'Jack'
console.log( obj.name); // Jack
// newObj不会随着obj的改变而改变
console.log(newObj.name) // Tom
-
但是展开运算符只能展开一层。
-
当进行对
嵌套对象
拷贝时,内层对象属性和方法会出现和直接赋值一样的问题。
const obj = {
name: 'Tom',
friend: {
name: 'Bob'
}
}
const newObj = { ...obj }
console.log(newObj.friend.name); // Bob
obj.friend.name = 'Jack'
console.log(obj.friend.name); // Jack
// 内层对象属性和方法依然会随着obj改变
console.log(newObj.friend.name); // Jack
2.浅拷贝
2.1.assign浅拷贝对象
const obj = {
name: 'Tom',
friend: {
name: 'Bob'
}
}
const newObj = {}
Object.assign(newObj, obj)
console.log(newObj.friend.name); // Bob
obj.friend.name = 'Jack'
console.log(obj.friend.name); // Jack
// 内层对象属性和方法依然会随着obj改变
console.log(newObj.friend.name); // Jack
2.2.concat浅拷贝数组
3.深拷贝
3.1.封装深拷贝函数
封装一个浅拷贝函数
const obj = {
name: 'Tom',
friend: {
name: 'Bob'
}
}
const newObj = {}
// 封装浅拷贝函数
function deepCopy(newObj, obj) {
for (let k in obj) {
// k是obj的属性名
// obj[k]是k对应是属性值
newObj[k] = obj[k]
}
}
deepCopy(newObj, obj)
console.log(newObj.friend); // Bob
obj.friend.name = 'Jack'
console.log(newObj.friend); // Jack
// 内层对象属性和方法依然会随着obj改变
console.log(obj.friend); // Jack
将浅拷贝函数升级成为深拷贝函数(利用递归)
const obj = {
name: 'Tom',
friend: {
name: 'Bob'
},
doing: ['sing', 'reading']
}
const newObj = {}
// 封装深拷贝函数
function deepCopy(newObj, obj) {
for (let k in obj) {
// 如果遍历到数组,进行递归
if (obj[k] instanceof Array) {
newObj[k] = [] // 创建新对象中的对应数组
deepCopy(newObj[k], obj[k])
}
// 如果遍历到对象,进行递归
else if (obj[k] instanceof Object) {
newObj[k] = {} // 创建新对象中的对应对象
deepCopy(newObj[k], obj[k])
}
// 简单数据类型直接拷贝
else {
newObj[k] = obj[k]
}
}
}
deepCopy(newObj, obj)
console.log(newObj.friend.name); // Bob
obj.friend.name = 'Jack'
console.log(newObj.friend.name); // Bob
// 内层数组不会随着obj改变
console.log(obj.friend.name); // Jack
console.log(newObj.doing[0]); // sing
obj.doing[0] = 'rap'
console.log(newObj.doing[0]); // sing
// 内层数组不会随着obj改变
console.log(obj.doing[0]); // rap
- 注意一定要先判断是否为数组,再判断是否为对象
- 因为数组包含于对象(万物皆对象)
当先判断是否为对象时,如果遍历到数组,也会通过是否为对象的判断,从而引发错误
3.2.lodash深拷贝
lodash是一个一致性、模块化、高性能的JS使用工具库
<!-- 在使用时我已经将lodash的js文件下载到了本地 -->
<script src="../lodash.min.js"></script>
<script>
const obj = {
name: 'Tom',
friend: {
name: 'Bob'
},
doing: ['sing', 'reading']
}
const newObj = _.cloneDeep(obj)
// 测试
console.log(newObj.friend.name); // Bob
obj.friend.name = 'Jack'
console.log(newObj.friend.name); // Bob
// 内层数组不会随着obj改变
console.log(obj.friend.name); // Jack
console.log(newObj.doing[0]); // sing
obj.doing[0] = 'rap'
console.log(newObj.doing[0]); // sing
// 内层数组不会随着obj改变
console.log(obj.doing[0]); // rap
</script>
3.3.JSON深拷贝
利用JSON把对象转换为JSON字符串,再将字符串转化为对象
const obj = {
name: 'Tom',
friend: {
name: 'Bob'
},
doing: ['sing', 'reading']
}
// 把对象转换为JSON字符串,再将字符串转化为对象
const newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj.friend.name); // Bob
obj.friend.name = 'Jack'
console.log(newObj.friend.name); // Bob
// 内层数组不会随着obj改变
console.log(obj.friend.name); // Jack
console.log(newObj.doing[0]); // sing
obj.doing[0] = 'rap'
console.log(newObj.doing[0]); // sing
// 内层数组不会随着obj改变
console.log(obj.doing[0]); // rap