-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript基础心法——深浅拷贝 #20
Comments
写的非常好 |
有个问题请教下,如你所做,首层是进行了深拷贝的运算,为什么你在定义这个概念的时候却叫做首层浅拷贝? |
循环引用的情况没写上呢 |
用ES6方法改写如下 deepClone: source => {
let clone = Object.assign({}, source);
Object.keys(clone).forEach(
key => (clone[key] = typeof source[key] === 'object' ? deepClone(source[key]) : source[key])
);
return clone;
} |
好像有点道理,叫首层深拷贝是不是更贴切... |
你的数组没有做判断 const deepClone = source => {
let clone = source.constructor === Array ? source.concat() : Object.assign({},source);
Object.keys(clone).forEach(key => (clone[key] = typeof clone[key] === 'object' ? deepClone(clone[key]) : source[key]))
return clone
} |
你这里有一个小错误,cloneObj.c=[1,1,1];其实是引用指向了另外一个对象,而originObj.c的指向没变,所有cloneObj.c改变了值,不影响originObj.c的值,仍然进行的浅拷贝。如果你试试cloneObj.c.push(4) |
因为这个方法总体还只是一个浅拷贝,为了强调首层深拷贝。。 |
我感觉叫"首层深拷贝" |
`
` |
很感谢您的分享。另外,我认为您递归方法的深拷贝代码中,第六行代码是不必要的,因为该方法是递归啊,和第二行代码相比,它就是冗余代码了。 |
关于Object.assign(target, sources)方法,将源对象的属性值复制给目标对象,如果属性值是对象的引用,那么复制的属性值也是对象的引用。结合对象的扩展运算符...是首层浅拷贝,而...实质上是Object.assign({}, sources)的变形,我自己写个例子,我现在非常困惑。
其实,参数sources源对象复制到目标对象,是首层浅拷贝的吧?是不是我哪点不对,求大神帮我解解惑! |
我明白了,确实是复制了源对象的引用,但是返回值是新对象,所以修改目标对象首层的引用并不影响sources源对象。因此是首层浅拷贝。 |
递归的写法也没有做到真正的深拷贝呀。例如我一层有一个function,只是
|
还有正则和日期也无法实现拷贝 |
我也在纳闷,看到这里放心了!谢谢 |
浅拷贝和深拷贝都是对于JS中的引用类型而言的,浅拷贝就只是复制对象的引用,如果拷贝后的对象发生变化,原对象也会发生变化。只有深拷贝才是真正地对对象的拷贝。
前言
说到深浅拷贝,必须先提到的是JavaScript的数据类型,之前的一篇文章JavaScript基础心法——数据类型说的很清楚了,这里就不多说了。
需要知道的就是一点:JavaScript的数据类型分为基本数据类型和引用数据类型。
对于基本数据类型的拷贝,并没有深浅拷贝的区别,我们所说的深浅拷贝都是对于引用数据类型而言的。
浅拷贝
浅拷贝的意思就是只复制引用,而未复制真正的值。
上面的代码是最简单的利用
=
赋值操作符实现了一个浅拷贝,可以很清楚的看到,随着cloneArray
和cloneObj
改变,originArray
和originObj
也随着发生了变化。深拷贝
深拷贝就是对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了。
只要进行了深拷贝,它们老死不相往来,谁也不会影响谁。
目前实现深拷贝的方法不多,主要是两种:
JSON
对象中的parse
和stringify
JSON.stringify/parse的方法
先看看这两个方法吧:
JSON.stringify
是将一个JavaScript
值转成一个JSON
字符串。JSON.parse
是将一个JSON
字符串转成一个JavaScript
值或对象。很好理解吧,就是
JavaScript
值和JSON
字符串的相互转换。它能实现深拷贝呢?我们来试试。
确实是深拷贝,也很方便。但是,这个方法只能适用于一些简单的情况。比如下面这样的一个对象就不适用:
发现在
cloneObj
中,有属性丢失了。。。那是为什么呢?在
MDN
上找到了原因:undefined
、function
、symbol
会在转换过程中被忽略。。。明白了吧,就是说如果对象中含有一个函数时(很常见),就不能用这个方法进行深拷贝。
递归的方法
递归的思想就很简单了,就是对每一层的数据都实现一次
创建对象->对象赋值
的操作,简单粗暴上代码:我们来试试:
可以。那再试试带有函数的:
也可以。搞定。
是不是以为这样就完了?? 当然不是。
JavaScript中的拷贝方法
我们知道在
JavaScript
中,数组有两个方法concat
和slice
是可以实现对原数组的拷贝的,这两个方法都不会修改原数组,而是返回一个修改后的新数组。同时,ES6 中 引入了
Object.assgn
方法和...
展开运算符也能实现对对象的拷贝。那它们是浅拷贝还是深拷贝呢?
concat
该方法可以连接两个或者更多的数组,但是它不会修改已存在的数组,而是返回一个新数组。
看着这意思,很像是深拷贝啊,我们来试试:
看上去是深拷贝的。
我们来考虑一个问题,如果这个对象是多层的,会怎样。
originArray
中含有数组[1,2,3]
和对象{a:1}
,如果我们直接修改数组和对象,不会影响originArray
,但是我们修改数组[1,2,3]
或对象{a:1}
时,发现originArray
也发生了变化。结论:
concat
只是对数组的第一层进行深拷贝。slice
解释中都直接写道是
a shallow copy
了 ~但是,并不是!
同样地,我们试试多层的数组。
果然,结果和
concat
是一样的。结论:
slice
只是对数组的第一层进行深拷贝。Object.assign()
复制复制复制。
那到底是浅拷贝还是深拷贝呢?
自己试试吧。。
结论:
Object.assign()
拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。... 展开运算符
结论:
...
实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。首层浅拷贝
我们知道了,会有一种情况,就是对目标对象的第一层进行深拷贝,然后后面的是浅拷贝,可以称作“首层浅拷贝”。
我们可以自己实现一个这样的函数:
我们来测试一下:
经过上面的修改,
cloneObj
不用说,肯定是{a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}}
了,那originObj
呢?刚刚我们验证了cloneObj === originObj
是false
,说明这两个对象引用地址不同啊,那应该就是修改了cloneObj
并不影响originObj
。What happend?
originObj
中关于a
、c
都没被影响,但是d
中的一个对象被修改了。。。说好的深拷贝呢?不是引用地址都不一样了吗?原来是这样:
shallowClone
的代码中我们可以看出,我们只对第一层的目标进行了深拷贝
,而第二层开始的目标我们是直接利用=
赋值操作符进行拷贝的。总结
=
实现的是浅拷贝,只拷贝对象的引用值;JSON.stringify
实现的是深拷贝,但是对目标对象有要求;The text was updated successfully, but these errors were encountered: