javascript中的对象类型拷贝问题
在这篇文章中,我们将讨论 对象类型(objects)
、或者说引用类型在 javascript
中的拷贝问题,会分别对 浅拷贝
和 深拷贝
进行说明。
在开始之前,我们需要了解一些基础知识:Objects
在 javascript
中只是一块内存地址的简单引用,任何变量都可以指向这块地址,以至于有的时候,这使其变得难以琢磨,比如,将一个对象的引用复制给另一个变量,此时,这块地址就会有2处引用。
1 | var foo = { |
如上面代码,我们声明了一个 foo
变量,该变量指向 { a: "abc"}
对象所在的内存地址,此时,可以使用 foo.a
查看其中的属性 a
的值为 abc
。之后,我们又将 foo
赋值给了 bar
变量,即将 { a: "abc"}
对象的地址赋值给了 bar
变量,这时候,该对象就被2个变量所引用,随后,我们执行 foo.a = "yo foo"
改变了该对象中 a
属性的值,再次打印 foo.a
和 bar.a
后显示的结果都是 yo foo
。同理,修改 bar.a
得到的也是一样的结果,因为他们改变的都是同一个对象的属性值。
浅拷贝(shallow copy)
如果你的对象里所有的属性值都是 值类型
,那么你可以使用 es6
中新的对象API Object.assign()
或者使用扩展运算符 ...
进行拷贝操作,俗称 浅拷贝
。
1 | var obj = { foo: "foo", bar: "bar" }; |
1 | var obj = { foo: "foo", bar: "bar" }; |
上面的两种方式都可以将多个源对象中的属性拷贝到目标对象上。
1 | var obj1 = { foo: "foo" }; |
但问题是,对于上面的两种方式,如果被拷贝的对象中的属性值也是一个对象类型,那么,对于该属性的拷贝只是拷贝一个对象的引用,这跟第一个例子是一样的情况 var bar = foo;
:
1 | var foo = { a: 0 , b: { c: 0 } }; |
也就是说,你并没有拷贝出一个副本出来,不管对哪个变量进行了修改,那个属性值为对象类型的属性都会被改变。
深拷贝(deep copy)
为了能够 深拷贝
一个对象类型的值,一种解决方法是,首先将该对象序列化成一个 JSON
字符串,然后再将其解析回来。
1 | var obj = { a: 0, b: { c: 0 } }; |
不幸的是,这种方式只对那些可以序列化为 JSON
格式的数据有用,并且要求这些属性值不存在任何的循环引用。比如,Date
对象,当你对其进行 字符串化 后,再解析回来的时候,该数据就只是一个时间字符串了,而不是原先的 Date
对象。
1 | var d = new Date() // Wed Apr 10 2019 16:49:10 GMT+0800 |
最终解决
综上所述,我们需要对不同的数据类型进行处理,所以有如下方法:
1 | function deepClone(obj) { |
具体思路,就是针对不同类型的数据做不同的拷贝处理,对于有循环引用的数据进行递归操作。上面的函数可以直接应用在实际项目中。