你只须要一行黑科技代码就能够实现深拷贝
var copyObj = { name: 'ziwei', arr : [1,2,3] } var targetObj = JSON.parse(JSON.stringify(copyObj)) 此时 copyObj.arr !== targetObj.arr 已经实现了深拷贝
别着急走,利用window.JSON的方法作深拷贝存在2个缺点:
固然,你明确知道他们的缺点后,若是他的缺点对你的业务需求没有影响,就能够放心使用了,一行原生代码就能搞定。面试
目前我在开发业务场景中,大多还真能够忽略上面2个缺点。每每须要深拷贝的对象里没有函数,也不须要拷贝它原型链的属性。api
下面我会尽量全面的讲解清楚JS里对象的拷贝,要讲清楚拷贝,你须要一点点前置知识数组
你须要的前置知识:数据结构
关于对象拷贝的所有:框架
咱们讨论JS对象深拷贝、浅拷贝的前提
只有对象里嵌套对象的状况下,才会根据需求讨论,咱们要深拷贝仍是浅拷贝。函数
好比下面这种对象spa
var obj1 = { name: 'ziwei', arr : [1,2,3] }
由于,若是是相似这样{name: 'ziwei'},没有嵌套对象的对象的话,就不必区分深浅拷贝了。只有在有嵌套的对象时,深拷贝和浅拷贝才有区别code
浅拷贝是什么样子的 (咱们暂时无论具体如何实现,由于下面会单讲)
调用shallowCopy()后,obj2拷贝obj1全部的属性。可是obj2.arr和obj1.arr是不一样的引用,指向同一个内存空间对象
var obj2 = shallowCopy( obj1 , {}) console.log( obj1 !== obj2 ) // true 不管哪一种拷贝,obj1和obj2必定都是2个不一样的对象(内存空间不一样) console.log( obj2.arr === obj1.arr ) // true 他们2个对象里arr的引用,指向【相同的】内存空间
因此, 2个obj通过拷贝后,虽然他们属性相同,也的确是不一样的对象,但他们内部的obj都是指向同一个内存空间,这种咱们叫浅拷贝blog
深拷贝是什么样子的 (咱们暂时无论具体如何实现,由于下面会单讲)
调用deepCopy()后,obj2拷贝obj1全部的属性,并且obj2.arr和obj1.arr是指向不一样的内存空间,
2个obj2除了拷贝了同样的属性,没有任何其余关联。
var obj2 = deepCopy( obj1 , {}) console.log( obj1 !== obj2 ) // true 不管哪一种拷贝,obj1和obj2必定都是2个不一样的对象(内存空间不一样) console.log( obj2.arr === obj1.arr ) // false 他们2个对象里arr的引用,指向【不一样的】内存空间
因此, 2个obj通过拷贝后,除了拷贝下来相同的属性以外,没有任何其余关联的2个对象,这种咱们叫深拷贝
举个栗子,业务需求是 : 一个表格展现商品各类信息,点击【赞成】时,是能够弹出对话框调整商品数量的。
这种业务需求下,咱们就会用到对象的深拷贝。由于【商品表格】的属性和【调整商品表格】的属性几乎同样,咱们须要拷贝。
下面的伪代码和图片就是展现使用浅拷贝存在的问题
这样获得的adjustTableArr和tableArr里,内部对象都是相同的,因此就出现了图中红线标注的状况,
当咱们修改【调整商品表格】里的商品数量时,【商品表格】也跟着改变了,这并非咱们想要的
// 表格对象的数据结构 var tableArr = [ {goods_name : '长袖腰背夹' , code : 'M216C239E0864' , num : '2'}, {goods_name : '长袖腰背夹' , code : 'M216C240B0170' , num : '3'}, {goods_name : '短塑裤' , code : 'M216D241C04106' , num : '3'}, ] var adjustTableArr = [] // 调整表格用的数组 for (var key in tableArr) { // 浅拷贝 adjustTableArr[key] = tableArr[key] }
而实际上,咱们但愿这2个表格里的数据彻底独立,互不干扰,只有在确认调整以后才刷新商品数量。
这种状况下咱们就可使用前面说的深拷贝的一行黑科技
var adjustTableArr = JSON.parse(JSON.stringify(tableArr))
还记得它的缺陷吗? 对象里的函数没法被拷贝,原型链里的属性没法被拷贝。这里就对业务没有影响,能够很方便的深拷贝。
其实JQ里已经有$.extend()函数,实现就是深拷贝和浅拷贝的功能。有兴趣的小伙伴也能够看看源码。
浅拷贝
浅拷贝比较简单,就是用for in 循环赋值
function shallowCopy(source, target = {}) { var key; for (key in source) { if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的属性,我不拷贝 target[key] = source[key]; } } return target; }
深拷贝的实现
function deepCopy(source, target = {}) { var key; for (key in source) { if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的属性,我不拷贝 if (typeof(source[key]) === "object") { // 若是这一项是object类型,就递归调用deepCopy target[key] = Array.isArray(source[key]) ? [] : {}; deepCopy(source[key], target[key]); } else { // 若是不是object类型,就直接赋值拷贝 target[key] = source[key]; } } } return target; }
以上的不管深、浅拷贝,都用了source.hasOwnProperty(key),意思是判断这一项是不是其自有属性,是的话才拷贝,不是就不拷贝。
也就是说__proto__上面的属性,我不拷贝。这个其实你能够根据业务需求,来决定加上和这个条件
(JQ的$.extend()是会连__proto__上的属性也拷贝下来的,可是是直接拷贝到对象上,而不是放到以前的__proto__上)
虽然你们可能常常用框架提供的api来实现深拷贝。
这篇文章分享的目的,更多仍是但愿用一篇文章整理清楚深浅拷贝的含义、递归实现思路,以及小伙伴们若是使用了JSON.parse()这种黑科技,必定要清楚这样写的优缺点。
上面的deepCopy方法有漏洞,没有考虑source一开始就是数组的状况
下面是一个修改后版本
function deepCopy( source ) { let target = Array.isArray( source ) ? [] : {} for ( var k in source ) { if ( typeof source[ k ] === 'object' ) { target[ k ] = deepCopy( source[ k ] ) } else { target[ k ] = source[ k ] } } return target }