- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章详细分析JavaScript中的深浅拷贝由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
在说JS中深浅拷贝之前,我们需要对JS中的数据类型有所了解,分为基本数据类型与引用数据类型,对于基本数据类型并没有深浅拷贝的说法,深浅拷贝主要针对引用数据类型.
1、浅拷贝 。
浅拷贝只复制了引用,并没有复制值。在JS中最简单的浅拷贝就是利用“=”赋值操作符来实现.
1
2
3
4
5
6
7
8
9
10
11
|
var
obj1 = {
a:1,
b:[2,3,4],
c:{name:
'tanj'
},
fun:
function
(){
console.log(
'fun'
)
}
}
var
obj2 = obj1
obj2.a = 666
/* 修改obj2的值,obj1的值也随之改变 */
console.log(obj1)
/* {a: 666, b: Array(3), c: {…}, fun: ƒ} */
|
上述代码中,我们修改obj2的值,obj1的值也随之发生了改变,使用”=“只实现了浅拷贝.
2、深拷贝 。
深拷贝是对目标的完全拷贝,进行深拷贝后的两个值互不影响.
1. 利用JSON.stringify与JSON.parse方法 。
JSON.stringify将一个JavaScript值转换为JSON字符串; 。
JSON.parse将一个JSON字符串转换为JavaScript值.
1
2
3
4
5
6
7
8
|
var
obj1 = {
a:1,
b:[2,3,4],
c:{name:
'tanj'
},
}
var
obj2 = JSON.parse(JSON.stringify(obj1))
obj2.a = 12
console.log(obj1)
/* {a: 1, b: Array(3), c: {…}} */
|
修改obj2的值并没有影响到obj1中的属性值,显然,我们利用JSON.parse与JSON.stringify实现了深拷贝.
但是,真的可以这么简单的实现吗?我们来看看下面的例子! 。
1
2
3
4
5
6
7
8
9
10
11
12
|
var
obj1 = {
a:1,
b:[2,3,4],
c:{name:
'tanj'
},
fun:
function
(){
console.log(
'fun'
)
}
}
var
obj2 = JSON.parse(JSON.stringify(obj1))
obj2.a = 12
console.log(obj1)
/* {a: 1, b: Array(3), c: {…}, fun: ƒ} */
console.log(obj2)
/* {a: 12, b: Array(3), c: {…}} */
|
转换后的obj2中没有了fun这个属性,这是由于在利用JSON.stringify转换过程中,忽略了undefined、function、symbol。显然,当我们的对象中出现这些类型的属性时无法利用该方法实现深拷贝.
2. 递归 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function
deepClone(source){
if
(!isObject(source))
return
source
var
newObj = source
instanceof
Array? []:{}
for
(let key
in
source){
if
(source.hasOwnProperty(key)){
newObj[key] = isObject(source[key])?deepClone(source[key]):source[key]
}
}
return
newObj
}
function
isObject(x) {
return
typeof
x ===
'object'
&& x !=
null
}
|
测试一下上述方法:
1
2
3
4
5
6
7
8
9
10
11
|
var
obj1 = {
a:1,
b:[2,3,4],
c:{name:
'tanj'
},
fun:
function
(){
console.log(
'fun'
)
}
}
var
obj2 = deepClone(obj1)
obj2.a = 12
console.log(obj1)
/* {a: 1, b: Array(3), c: {…}, fun: ƒ} */
|
通过例子可以看到,我们修改了obj2中a属性的值,但是并没有影响到obj1中的a属性值。通过递归我们可以实现深拷贝! 。
注意:上述方法未解决循环引用的问题.
1
2
3
4
|
var
obj1 = {}
obj1.a = obj1
var
obj2 = deepClone(obj1)
/* 报错,栈溢出 */
console.log(obj2)
|
关于如何解决循环引用问题以及实现Symbol类型拷贝,稍后完善.
3、其他拷贝方法 。
1. 数组中的concat()和slice()方法 。
我们知道数组中有两个方法concat和slice可以完成复制数组,并且返回新数组。以concat为例.
1
2
3
4
5
|
var
arr = [1,2,3]
var
arr2 = arr.concat()
arr2[2]=4
console.log(arr)
/* [1, 2, 3] */
console.log(arr2)
/* [1, 2, 4] */
|
改变arr2的值,并没有影响到arr的值,这是实现了数组的深拷贝吗,先不急于下结论,一起看看下面的例子再来分析:
1
2
3
4
5
6
|
var
arr = [1,2,3,[4,5,6],{a:7}]
var
arr2 = arr.concat()
arr2[3] = 444
arr2[4].a=8
console.log(arr)
/* [1,2,3,[4,5,6],{a:8}] */
console.log(arr2)
/* [1,2,3,444,{a:8}] */
|
我们直接修改arr2[3],并没有引起arr的改变,但是我们修改arr2[4].a时,arr中的相应元素跟着一起发生了改变。其实,我们对arr2数组中的元素直接进行改变(比如:arr2[0]=***,arr2[1]=***,arr2[3]=***)时,不会影响到原数组arr,但是我们修改数组中的[3,4,5]或{a:7}时,会造成原数组arr的改变.
结论:concat()方法对数组第一层进行了深拷贝.
可以再试试数组的slice()方法,它也是只对数组第一层进行了深拷贝.
2. Object.assign()和...展开运算符 。
1
2
3
4
5
6
7
8
9
|
var
obj1 = {
a:1,
b:[2,3,4],
c:{name:
'tanj'
}
}
var
obj2 = {...obj1}
obj2.a = 666
obj2.c.name =
'xinxin'
console.log(obj1)
/* {a:1,b:[2,3,4],c:{name:'xinxin'}} */
|
可以看到利用...展开运算符实现的是对象第一层的深拷贝。后面的只是拷贝的引用值.
可以试试Object.assign()方法:
1
2
3
4
5
6
7
8
9
10
|
var
obj1 = {
a:1,
b:[2,3,4],
c:{name:
'tanj'
}
}
var
obj2 = {}
Object.assign(obj2,obj1)
obj2.a = 666
obj2.b[0] = 0
console.log(obj1)
/* {a:1,b:[0,3,4],c:{name:'tanj'} */
|
同样,只对对象第一层进行了深拷贝,假如源对象的属性值(例如obj1)是一个指向对象的引用,obj2也只拷贝那个引用值。所以改变obj2中b所指向的那个数组时,obj1的值也会发生改变.
我们可以自己实现一个这样的效果,只对第一层进行深拷贝:
1
2
3
4
5
6
7
8
9
|
function
shallowClone(source) {
const newObj = source.constructor === Array ? [] : {}
for
(let keys
in
source) {
if
(source.hasOwnProperty(keys)) {
newObj[keys] = source[keys]
}
}
return
newObj
}
|
以上就是分析JavaScript中的深浅拷贝的详细内容,更多关于JavaScript 深浅拷贝的资料请关注我其它相关文章! 。
原文链接:https://www.tanj.com.cn/article/detail/5ebe10ee451b6422a1ea4909 。
最后此篇关于详细分析JavaScript中的深浅拷贝的文章就讲到这里了,如果你想了解更多关于详细分析JavaScript中的深浅拷贝的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我是一名优秀的程序员,十分优秀!