彻底理解JavaScript中的深拷贝和浅拷贝

console.log(obj1) //{x: 1, y: 2}

console.log(obj2) //{x: 1, y: 2}

obj2.x = 2; //修改obj2.x

console.log(obj1) //{x: 2, y: 2}

console.log(obj2) //{x: 2, y: 2}

可以看到基本类型的 num1 和引用类型的 obj1 是有区别的。

基本类型和引用类型


ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置。而引用类型值是指那些保存堆内存中的对象,意思是变量中保存的实际只是这个对象的引用,这个引用指向堆内存中的对象。

目前基本类型有:Boolean、Null、Undefined、Number、String、Symbol。

引用类型有:Object、Array、Function。

再回到前面的案例,案例1中的值为基本类型,案例2中的值为引用类型。案例2中的赋值就是典型的浅拷贝,并且深拷贝与浅拷贝的概念只存在于引用类型。

深拷贝与浅拷贝


既然已经知道了深拷贝与浅拷贝的来由,那么该如何实现深拷贝?我们先分别看看Array和Object自有方法是否支持:

Array

var arr1 = [1, 2], arr2 = arr1.slice();

console.log(arr1); //[1, 2]

console.log(arr2); //[1, 2]

arr2[0] = 3; //修改arr2

console.log(arr1); //[1, 2]

console.log(arr2); //[3, 2]

此时,arr2的修改并没有影响到arr1,我们把arr1改成二维数组再来看看:

var arr1 = [1, 2, [3, 4]], arr2 = arr1.slice();

console.log(arr1); //[1, 2, [3, 4]]

console.log(arr2); //[1, 2, [3, 4]]

arr2[2][1] = 5;

console.log(arr1); //[1, 2, [3, 5]]

console.log(arr2); //[1, 2, [3, 5]]

咦,arr2有改变了arr1,看来slice()只能实现一维数组的深拷贝。

具备同等特性的还有:concatArray.from()

Object

1、Object.assign()

var obj1 = {x: 1, y: 2}, obj2 = Object.assign({}, obj1);

console.log(obj1) //{x: 1, y: 2}

console.log(obj2) //{x: 1, y: 2}

obj2.x = 2; //修改obj2.x

console.log(obj1) //{x: 1, y: 2}

console.log(obj2) //{x: 2, y: 2}

var obj1 = {

x: 1,

y: {

m: 1

}

};

var obj2 = Object.assign({}, obj1);

console.log(obj1) //{x: 1, y: {m: 1}}

console.log(obj2) //{x: 1, y: {m: 1}}

obj2.y.m = 2; //修改obj2.y.m

console.log(obj1) //{x: 1, y: {m: 2}}

console.log(obj2) //{x: 2, y: {m: 2}}

经测试,Object.assign()也只能实现一维对象的深拷贝

2、JSON.parse(JSON.stringify(obj))

var obj1 = {

x: 1,

y: {

m: 1

}

};

var obj2 = JSON.parse(JSON.stringify(obj1));

console.log(obj1) //{x: 1, y: {m: 1}}

console.log(obj2) //{x: 1, y: {m: 1}}

obj2.y.m = 2; //修改obj2.y.m

console.log(obj1) //{x: 1, y: {m: 1}}

console.log(obj2) //{x: 1, y: {m: 2}}

JSON.parse(JSON.stringify(obj))看起来在实现深拷贝上做的不错,不过 MDN 文档的描述有句话写的很清楚:

undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。

我们再来把 obj1 改造下:

var obj1 = {

x: 1,

y: undefined,

z: function add(z1, z2) {

return z1 + z2

},

a: Symbol(‘foo’)

};

var obj2 = JSON.parse(JSON.stringify(obj1));

console.log(obj1); //{x: 1, y: undefined, z: f, a: Symbol(foo)}

console.log(JSON.stringify(obj1)); //{“x”: 1}

console.log(obj2) //{x: 1}

发现,在将 obj1 进行 JSON.stringify() 序列化的过程中,y、z、a都被忽略了,也就验证了 MDN 文档的描述,既然这样,那 JSON.parse(JSON.stringify(obj)) 的使用也是有局限性的,不能深拷贝含有undefined、function、symbol值的对象,不过 JSON.parse(JSON.stringify(obj)) 简单粗暴,已经满足 90% 的使用场景了。

经过验证,我们发现 JS 提供的自有方法并不能彻底解决 Array、Object 的深拷贝问题。只能祭出大杀器:递归

function deepCopy(obj) {

// 创建一个新对象

let result = {}

let keys = Object.keys(obj),

key = null,

temp = null;

for (let i = 0; i < keys.length; i++) {

key = keys[i];

temp = obj[key];

// 如果字段的值也是一个对象则递归操作

if (temp && typeof temp === ‘object’) {

result[key] = deepCopy(temp);

} else {

// 否则直接赋值给新对象

result[key] = temp;

}

}

return result;

}

var obj1 = {

x: {

m: 1

},

y: undefined,

z: function add(z1, z2) {

return z1 + z2

},

a: Symbol(“foo”)

};

var obj2 = deepCopy(obj1);

obj2.x.m = 2;

console.log(obj1); //{x: {m: 1}, y: undefined, z: ƒ, a: Symbol(foo)}

console.log(obj2); //{x: {m: 2}, y: undefined, z: ƒ, a: Symbol(foo)}

可以看到,递归完美的解决了前面遗留的所有问题,我们也可以用第三方库: jquery$.extendlodash_.cloneDeep 来解决深拷贝。上面虽然使用 Object 验证,但对于 Array 也同样使用,因为 Array 也是特殊的 Object。

但是,还有一个非常特殊的场景:

循环引用拷贝

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档,需要的读者可以戳这里免费领取!

)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

[外链图片转存中…(img-AQhKQYU8-1713783709591)]

最后

前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档,需要的读者可以戳这里免费领取!

[外链图片转存中…(img-LbxKQEcM-1713783709591)]

[外链图片转存中…(img-4YKrWbQo-1713783709591)]

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值