- 浅拷贝:只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做浅拷贝(浅复制)。浅拷贝只复制指向某个对象的指针(引用地址),而不复制对象本身,新旧对象还是共享同一块内存。
- 深拷贝:在堆中重新分配内存,并且把源对象所有属性都进行新建拷贝,以保证深拷贝的对象的引用图不包含任何原有对象或对象图上的任何对象,拷贝后的对象与原来的对象是完全隔离,互不影响。
浅拷贝方法
1. 直接赋值
直接赋值是最常见的一种浅拷贝方式。例如:
var stu = {
name: 'xiejie',
age: 18
}
// 直接赋值
var stu2 = stu;
stu2.name = "zhangsan";
console.log(stu); // { name: 'zhangsan', age: 18 }
console.log(stu2); // { name: 'zhangsan', age: 18 }
2. Object.assign 方法
我们先来看一下 Object.assign 方法的基本用法。
该方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。
如下:
const stu = {
name: 'lisi',
age: 18,
stuInfo: {
No: 1,
score: 100
}
}
const stu2 = Object.assign({}, stu)
stu2.name = 'zhangsan';
stu2.stuInfo.score = 90;
console.log(stu); // { name: 'lisi', age: 18, stuInfo: { No: 1, score: 90 } }
console.log(stu2); // { name: 'zhangsan', age: 18, stuInfo: { No: 1, score: 90 } }
Object.assign 方法事实上是一个浅拷贝。
当对象的属性值对应的是一个对象时,该方法拷贝的是对象的属性的引用,而不是对象本身。stu2.stuInfo.score修改成一样的。
3. ES6 扩展运算符
const stu = {
name: 'lisi',
age: 18,
stuInfo: {
No: 1,
score: 100
}
}
const stu2 = {...stu}
stu2.name = 'zhangsan';
stu2.stuInfo.score = 90;
console.log(stu); // { name: 'lisi', age: 18, stuInfo: { No: 1, score: 90 } }
console.log(stu2); // { name: 'zhangsan', age: 18, stuInfo: { No: 1, score: 90 } }
4. 数组的 slice 和 concat 方法
在 javascript 中,数组也是一种对象,所以也会涉及到深浅拷贝的问题。
在 Array 中的 slice 和 concat 方法,不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。
// concat 拷贝数组
var arr1 = [1, true, 'Hello', { name: 'lisi', age: 18 }];
var arr2 = arr1.concat();
console.log(arr1); // [ 1, true, 'Hello', { name: 'lisi', age: 18 } ]
console.log(arr2); // [ 1, true, 'Hello', { name: 'lisi', age: 18 } ]
arr2[0] = 2;
arr2[3].age = 19;
console.log(arr1); // [ 1, true, 'Hello', { name: 'lisi', age: 19 } ]
console.log(arr2); // [ 2, true, 'Hello', { name: 'lisi', age: 19 } ]
// concat 拷贝数组
var arr1 = [1, true, 'Hello', { name: 'lisi', age: 18 }];
var arr2 = arr1.slice();
console.log(arr1); // [ 1, true, 'Hello', { name: 'lisi', age: 18 } ]
console.log(arr2); // [ 1, true, 'Hello', { name: 'lisi', age: 18 } ]
arr2[0] = 2;
arr2[3].age = 19;
console.log(arr1); // [ 1, true, 'Hello', { name: 'lisi', age: 19 } ]
console.log(arr2); // [ 2, true, 'Hello', { name: 'lisi', age: 19 } ]
5. jQuery 中的 $.extend
在 jQuery 中,$.extend(deep,target,object1,objectN) 方法可以进行深浅拷贝。各参数说明如下:
- deep:如过设为 true 为深拷贝,默认是 false 浅拷贝
- target:要拷贝的目标对象
- object1:待拷贝到第一个对象的对象
- objectN:待拷贝到第N个对象的对象
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
const obj = {
name: 'wade',
age: 37,
friend: {
name: 'james',
age: 34
}
}
const cloneObj = {};
// deep 默认为 false 为浅拷贝
$.extend(cloneObj, obj);
obj.friend.name = 'rose';
console.log(obj);
console.log(cloneObj);
</script>
</body>

深拷贝方法
1. JSON.parse(JSON.stringify)
这是一个广为流传的深拷贝方式,用 JSON.stringify 将对象转成 JSON 字符串,再用 JSON.parse 方法把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
示例如下:
const stu = {
name: 'lisi',
age: 18,
stuInfo: {
No: 1,
score: 100
}
}
const stu2 = JSON.parse(JSON.stringify(stu));
stu2.name = 'zhangsan';
stu2.stuInfo.score = 90;
console.log(stu); // { name: 'lisi', age: 18, stuInfo: { No: 1, score: 100 } }
console.log(stu2); // { name: 'zhangsan', age: 18, stuInfo: { No: 1, score: 90 } }
这种方式看似能够解决问题,但是这种方法也有一个缺点,那就是不能处理函数。
这是因为 JSON.stringify 方法是将一个 javascript 值(对象或者数组)转换为一个 JSON 字符串,而 JSON 字符串是不能够接受函数的。同样,正则对象也一样,在 JSON.parse 解析时会发生错误。
例如:
const stu = {
name: 'lisi',
age: 18,
stuInfo: {
No: 1,
score: 100,
saySth: function () {
console.log('我是一个学生');
}
}
}
const stu2 = JSON.parse(JSON.stringify(stu));
stu2.name = 'zhangsan';
stu2.stuInfo.score = 90;
console.log(stu); // { name: 'lisi', age: 18, stuInfo: { No: 1, score: 100, saySth: [Function: saySth] }}
console.log(stu2); // { name: 'zhangsan', age: 18, stuInfo: { No: 1, score: 90 } }
可以看到,在原对象中有方法,拷贝之后,新对象中没有方法了。
2. $.extend(deep,target,object1,objectN)
前面在介绍浅拷贝时提到了 jQuery 的这个方法,该方法既能实现浅拷贝,也能实现深拷贝。要实现深拷贝,只需要将第一个参数设置为 true 即可。
3. 手写递归方法
function deepClone(target) {
var result;
// 判断是否是对象类型
if (typeof target === 'object') {
// 判断是否是数组类型
if (Array.isArray(target)) {
result = []; // 如果是数组,创建一个空数组
// 遍历数组的键
for (var i in target) {
// 递归调用
result.push(deepClone(target[i]))
}
} else if (target === null) {
// 再判断是否是 null
// 如果是,直接等于 null
result = null;
} else if (target.constructor === RegExp) {
// 判断是否是正则对象
// 如果是,直接赋值拷贝
result = target;
} else if (target.constructor === Date) {
// 判断是否是日期对象
// 如果是,直接赋值拷贝
result = target;
} else {
// 则是对象
// 创建一个空对象
result = {};
// 遍历该对象的每一个键
for (var i in target) {
// 递归调用
result[i] = deepClone(target[i]);
}
}
} else {
// 表示不是对象类型,则是简单数据类型 直接赋值
result = target;
}
// 返回结果
return result;
}




