一、概念
1.1 浅拷贝
浅拷贝:只拷贝对象中的基本类型的数据,而引用类型数据,复制后也是会发生引用。
简单来说,浅拷贝只复制指向某个对象的指针,这个指针指向对象在内存中保存的地址,并不会复制对象本身,新旧对象共享同一块内存。
示意图如图所示:
其中,0x2000代表对象在内存中的地址,obj1与obj2共同指向0x2000。
1.2 深拷贝
深拷贝:将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上,并开辟了一块新的内存地址来存放复制的对象。
示意图如图所示:
其中,0x2000代表旧对象在内存中的地址,0x3000代表复制出的新对象在内存的地址,obj1与obj2分别指向0x2000与0x3000。
二、拷贝方式
2.1 直接赋值
2.1.1 基本数据类型的拷贝
//
let obj1 = {
name: 'zs',
age: 18,
gender: '男',
}
console.log(obj1)
let obj2 = obj1
obj2.name = 'ls'
console.log(obj2)
输出结果:
根据结果可以得知:对于只有一层基本类型数据的对象而言,是深拷贝。
2.1.2 引用类型的拷贝
// 新建obj1对象
let obj1 = {
name: 'zs',
age: 18,
gender: '男',
score: {
Math: 80,
English: 90
}
}
console.log(obj1)
let obj2 = obj1
obj2.score.Math = 85
console.log(obj2)
输出结果:
根据输出结果可以得知:改动拷贝对象obj2.score.Math的值为85后,原对象obj1.score.Math的值也被改为了85。这也是浅拷贝
直接赋值的方式虽然简单,但是只能深拷贝第一层的基本类型数据,引用类型还是浅拷贝。
2.2 Object.assign()方法
在ES6中,关于对象的复制,则提出了Object.assign()方法,该方法只能实现浅复制。
let obj1 = {
name: 'zs',
age: 18,
gender: '男',
score: {
Math: 80,
English: 90
}
}
console.log(obj1)
let obj2 = {}
Object.assign(obj2, obj1)
obj2.score.Math = 85
console.log(obj2)
输出结果
2.3 ES6中的扩展运算符(…)
let obj1 = {
name: 'zs',
age: 18,
gender: '男',
score: {
Math: 80,
English: 90
}
}
console.log(obj1)
let obj2 = {...obj1}
obj2.score.Math = 85
obj2.name = 'ls'
console.log(obj2)
输出结果:
根据输出结果可以看到obj1和obj2的各自的name属性被修改了,但是score.Math仍然共同一个数据,此为浅拷贝。
2.4 jQuery中的 $.extend
语法格式:$.extend(deep,target,object1,objectN)
参数含义:
deep: 设为true代表深拷贝,默认是false,即浅拷贝
target: 要拷贝的目标对象
object1: 待拷贝到第一个对象的对象
objectN: 待拷贝到第N个对象的对象
let obj1 = {
name: 'zs',
age: 18,
gender: '男',
score: {
Math: 80,
English: 90
}
}
console.log(obj1)
let obj2 = {}
$.extend(obj2, obj1) // 浅拷贝
obj2.score.Math = 85
console.log(obj2)
输出结果:
let obj1 = {
name: 'zs',
age: 18,
gender: '男',
score: {
Math: 80,
English: 90
}
}
console.log(obj1)
let obj2 = {}
$.extend(true, obj2, obj1) // 深拷贝
obj2.score.Math = 85
console.log(obj2)
输出结果:
利用前端框架JQuery的方法性能很高,使用起来也很容易,但每次要使用都需要导入这个库,算是一个美中不足的地方了。
2.5 递归方式
function deepCopy(oldObj, newobj) {
for (var key in oldObj) {
var item = oldObj[key];
// 判断是否是对象
if (item instanceof Object) {
newobj[key] = {}; //定义一个空的对象来接收拷贝的内容
deepCopy(item, newobj[key]); //递归调用
// 判断是否是数组
} else if (item instanceof Array) {
newobj[key] = []; //定义一个空的数组来接收拷贝的内容
deepCopy(item, newobj[key]); //递归调用
} else {
newobj[key] = oldObj[key];
}
}
}
let obj1 = {
name: 'zs',
age: 18,
gender: '男',
score: {
Math: 80,
English: 90
}
}
console.log(obj1);
let obj2 = {}
deepCopy(obj1, obj2)
obj2.score.Math = 85
console.log(obj2)
输出结果:
利用递归的方法完成对对象的深拷贝,虽然繁琐,代码也很长,但也是一种有效的方法。
2.6 JSON.parse(JSON.stringify())
原理:用JSON.stringify()将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,这是一个新的对象,而且对象会开辟新的栈,实现深拷贝。
let obj1 = {
name: 'zs',
age: 18,
gender: '男',
score: {
Math: 80,
English: 90
}
}
console.log(obj1);
let obj2 = JSON.parse(JSON.stringify(obj1))
obj2.score.Math = 85
console.log(obj2)
输出结果:
注意: 这种方法尽管可以实现对对象的深拷贝,但是会忽略掉原对象中定义的方法
let obj1 = {
name: 'zs',
age: 18,
gender: '男',
score: {
Math: 80,
English: 90
}
}
console.log(obj1);
let obj2 = JSON.parse(JSON.stringify(obj1))
obj2.score.Math = 85
console.log(obj2)
可以看到在obj2中没有fn这个方法,因为被忽略掉了。
三、学习总结
虽然拷贝对象的方式有很多,但是其拷贝的程度并不一致。在开发中,要根据业务需要,选择合适的方式来拷贝对象。
- 直接复制 实现的是浅拷贝,只拷贝对象的基本类型的数据
- 使用JS原生的递归方法是做复杂深拷贝的好方法,虽然代码繁琐,但兼顾了兼容性。
- 使用JSON的两种方法深拷贝只能是对象中没有函数时才能使用