js 浅拷贝和深拷贝算法、原理、区别

算法题目

什么是深拷贝、浅拷贝?如何实现深拷贝、浅拷贝算法?

算法详解

这题考查拷贝、数据引用等相关的知识点

【一】什么是拷贝?

我们可以理解拷贝是一种复制行为,比如

1
2
let a = 1;
let b = a;

这就是一种拷贝,变量a的值拷贝给变量b,从b也变为 1

1
2
let a = {}
let c = a;

这也是拷贝,变量c拥有和变量a同一个对象

【二】什么是深拷贝、浅拷贝

1、 JS有几种基础数据类型,比如数字型、布尔型、字符串、undefined、null、Symbol等,这些都是按值传递,如

1
2
var a = 1;
var b = a;

这个赋值拷贝,只是把变量 a 的值赋值给变量 b,这个时候,变量b得到的只是变量a所指示内存上的内容
如果我们继续执行这样一条语句:
b = 3;
这个变量b就会变成3,但是a仍然是1,a 和 b是相互对立的、互不影响的,这是按值传递

JS中还有引用类型数据,对象,数组等,这些是引用传递,如

1
2
let left = { a: 1 }
let right = left;

这个时候变量 a 和变量 b 指向同一个对象(同一个内存地址)
如果我们执行接下来的代码:
left.a = 5;
那这个时候right.a 也变成了 5,也就是说 a 和 b会相互影响的,他们指向同一个对象,这就是引用类型

【浅拷贝】只复制一层对象,当对象的属性是引用类型时,实质上复制的是其引用,当引用指向的值发生变化的时候,原对象属性值也跟着变化,相互还是会受到影响

【深拷贝】在拷贝的时候,创建新的对象,并把原对象所有属性都深拷贝被新对象,原属性如果是对象,也会重新创建新的对象并拷贝到新对象属性中,这样新旧对象,是相互对立的,互不影响,这个就是深拷贝

【三】浅拷贝算法

既然知道了浅拷贝,那我们来看一个浅拷贝的简单实现:

1
2
3
4
5
6
7
8
9
function shallowCopy(src) {
let dst = Array.isArray(src) ? [] : {};

// for in 遍历对象属性
for (let prop in src) {
dst[prop] = src[prop]
}
return dst;
}

当然,其实我们也可以直接使用赋值运算符 = ,也是简单的浅拷贝
还有,Object.assign方法也可以浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
let src = {
a: 1,
f: {
b: 2
}
}

let dst = shallowCopy(src)
dst.a = 100
dst.f.b = 200

src.a // 还是 1
src.f.b // 变成 200

【四】深拷贝算法

我们知道了深拷贝是把对象所有属性,如果属性也是对象的话,则递归属性对象,都进行深度拷贝复制

深拷贝也有很多种方法,我们介绍两种:
通过JSON方法这个实现也比较有意思,当然有比较大的缺点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let src = {
a: 2,
b: {
c: 3
},
get: function() {}
}

let dst = JSON.parse(JSON.stringify(src))
这样dst变成
{
a: 2,
b: {
c: 3
}
}

当修改src的属性的时候,dst没有任何变化,因此实现 了一种深拷贝的思想,但是有个巨大问题:
那就是丢失了对象方法 get

接下来我来看下通过递归方式实现的深拷贝吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function deepCopy(src) {
let dst = Array.isArray(src) ? [] : {}
if (src && typeof src === 'object') {
for (key in src) {
if (src.hasOwnProperty(key)) {
// 当熟悉是引用类型object时,则递归深拷贝
if (src[key] && typeof src[key] === 'object') {
dst[key] = deepCopy(src[key])
} else {
// 值类型,直接拷贝复制
dst[key] = src[key]
}
}
}
}
return dst
}

简单代码完成
主要是理解深拷贝是完全解除引用关系,产生完全新的对象,与原对象互不影响

 
更多前端面试题和算法题,请搜索微信小程序 前端面试精华
前端面试精华
前端面试精华

坚持原创技术分享,谢谢鼓励我继续创作!