浅拷贝
理解拷贝之前,需要了解什么是值类型和引用类型。这一点并不难。至少现象很多人都知道:
对值类型变量再复制一个,二者互不影响。
var a = 1;
var b = a; //浅拷贝
b = 2; //改变b的值,并不会影响到a,因为浅拷贝对基本类型而言就是值拷贝
console.log(a);
在对象类型变量中,二者会影响。(对象也叫引用数据类型),对象类型的浅拷贝则只是拷贝了地址。对象的指针地址都是指向栈内存的相同位置,修改其一,另一个也会变化
var p1 = {
name: 'jack'
}
var p2 = p1;
p2.name = 'rose';
console.log(p1);
深拷贝
我们肯定不希望两个对象类型互相影响。所以我们要对其进行深拷贝,就是创建一个新的对象,里面的值完全复刻原来的对象,例如下面代码:
方法1.1
var p1 = {
name: 'jack'
}
var p2 = {
name: p1.name
};
p2.name = 'rose';
这是第一种方法。我们用这种方法实现深拷贝是可以的,但是如果属性太多,一个个写就会很繁琐。于是可以通过for循环遍历属性来优化,如下面代码:
方法1.2
var person={
name:"张三",
age:22
}
var person1={};
for( key in person){
console.log(key);
person1[key]=person[key];
}
console.log(person);
console.log(person1);
这种做法有一个缺点,就是:person对象的属性都是基本数据类型,如果是引用类型呢?比如数组,对象呢?直接说结论:如果属性有引用类型的数据,二者又互相影响了。
方法1.3
这里面先说一个API。Object.assign()是一种可以对非嵌套对象进行深拷贝的方法,如果对象中出现嵌套情况,那么其对被嵌套对象的行为就成了普通的浅拷贝。如果没有嵌套,是可以用这个方法的。
var p1 = {
name: 'jack'
}
var p2 = {}
Object.assign(p2,p1);
但这个方法依然没解决属性中有引用类型的问题。先举一个对象嵌套对象的例子,如下面代码:
var person={
name:"张三",
age:22,
son:{
firstSon:"张大毛"
}
}
var person1={};
for( key in person){
console.log(key);
person1[key]=person[key];
}
console.log(person);
console.log(person1);
上文代码中,person对象的属性,son,也是一个对象。如果我们利用前面的所有方法拷贝一个person1出来,对person1的son做个修改,会发现最初的person对象里的“张大毛”也被修改了。
下面两个方法是解决对象嵌套的做法
方法1.4
这个方法是实战中常用的
var p1 = {
name: 'jack',
age:12
}
var p2 = JSON.parse(JSON.stringify(p1));
p2.name = 'rose';
利用了将JS对象转为JSON字符串,再转回来俩步骤。相当于一个序列化和反序列化的方法
展示下此方法结果
var p1 = {
name: '老张',
age:12,
son:{
firstson:'张大儿'
}
}
var p2 = JSON.parse(JSON.stringify(p1));
p2.name = '老李';
p2.son.firstson ='李大儿'
console.log(p2)
console.log(p1)
输出p2和p1,见下图
![](https://img-blog.csdnimg.cn/img_convert/2adee9f2ac4c1d86187ff50cc4c23bd9.png)
方法2
个人感觉面试时,也算比较常见的一道题,就是深浅拷贝,肯定是能自己实现更好。那么解决对象嵌套的问题,其实就是递归遍历,查看对象中的属性,是否还有引用类型的变量。
形参,源对象或数组
第一步先做校验:
不是对象或数组就没必要拷贝了
判断是对象还是数组,初始化结果
拿到数组和对象的每个key
核心代码就result[key]=obj[k],但注意要用递归
数组也能用对象的遍历for in形式,但实际中其他场景数组遍历还是用length
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
// obj 是 null ,或者不是对象和数组,直接返回
return obj
}
// 初始化返回结果
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
for (let key in obj) {
// 保证 key 不是原型的属性
if (obj.hasOwnProperty(key)) {
// 递归调用!!!
result[key] = deepClone(obj[key])
}
}
// 返回结果
return result
}
递归中,给deepClone,传的参数,其实是对象属性对应的值。假如,值是基本类型。在第一步校验的时候,就return obj了,跳出内部的递归函数,再去判断对象中下一个属性对应的值
参考资料:
1)https://blog.csdn.net/weixin_39570751/article/details/123363926
2)https://blog.csdn.net/weixin_45745641/article/details/121510217
3)https://blog.csdn.net/u011863822/article/details/121547527
视频: