javascript深拷贝、浅拷贝和循环引用深入理解(2)

这里的父级引用指的是,当对象的某个属性,正是这个对象本身,此时我们如果进行深拷贝,可能会在子元素->父对象->子元素...这个循环中一直进行,导致栈溢出。比如下面这个例子:

const obj1 = { x: 1, y: 2 }; obj1.z = obj1; const obj2 = deepCopy1(obj1); \\栈溢出

解决办法是:只需要判断一个对象的字段是否引用了这个对象或这个对象的任意父级即可,可以修改上面的deepCopy函数:

function deepCopy2(obj, parent=null) { //创建一个新对象 let result = {}; let keys = Object.keys(obj), key = null, temp = null, _parent = parent; //该字段有父级则需要追溯该字段的父级 while(_parent) { //如果该字段引用了它的父级,则为循环引用 if(_parent.originParent === obj) { //循环引用返回同级的新对象 return _parent.currentParent; } _parent = _parent.parent } for(let i=0,len=keys.length;i<len;i++) { key = keys[i] temp = obj[key] // 如果字段的值也是一个新对象 if(temp && typeof temp === 'object') { result[key] = deepCopy(temp, { //递归执行深拷贝,将同级的待拷贝对象与新对象传递给parent,方便追溯循环引用 originParent: obj, currentParent: result, parent: parent }); } else { result[key] = temp; } } return result; } const obj1 = { x:1 } obj1.z = obj1; const obj2 = deepCopy2(obj1);

2. 同级引用

假设对象obj有a,b,c三个子对象,其中子对象c中有个属性d引用了对象obj下面的子对象a。

const obj= { a: { name: 'a' }, b: { name: 'b' }, c: { } }; c.d.e = obj.a;

此时c.d.e和obj.a 是相等的,因为它们引用的是同一个对象

console.log(c.d.e === obj.a); //true

如果我们调用上面的deepCopy2函数

const copy = deepCopy2(obj); console.log(copy.a); // 输出: {name: "a"} console.log(copy.d.e);// 输出: {name: "a"} console.log(copy.a === copy.d.e); // 输出: false

以上表现我们就可以看出,虽然opy.a 和copy.d.e在字面意义上是相等的,但二者并不是引用的同一个对象,这点上来看对象copy和原对象obj还是有差异的。

这种情况是因为obj.a并不在obj.d.e的父级对象链上,所以deepCopy2函数就无法检测到obj.d.e对obj.a也是一种引用关系,所以deepCopy2函数就将obj.a深拷贝的结果赋值给了copy.d.e。

解决方案:父级的引用是一种引用,非父级的引用也是一种引用,那么只要记录下对象A中的所有对象,并与新创建的对象一一对应即可。

function deepCopy3(obj) { // hash表,记录所有的对象的引用关系 let map = new WeakMap(); function dp(obj) { let result = null; let keys = Object.keys(obj); let key = null, temp = null, existobj = null; existobj = map.get(obj); //如果这个对象已经被记录则直接返回 if(existobj) { return existobj; } result = {} map.set(obj, result); for(let i =0,len=keys.length;i<len;i++) { key = keys[i]; temp = obj[key]; if(temp && typeof temp === 'object') { result[key] = dp(temp); }else { result[key] = temp; } } return result; } return dp(obj); } const obj= { a: { name: 'a' }, b: { name: 'b' }, c: { } }; c.d.e = obj.a; const copy = deepCopy3(obj);

五、总结

其实拷贝的方式还有很多种,比如jquery中的$.extend,lodash的_.cloneDeep等等,关于拷贝中还有很多问题值得深究,比如正则类型的值如何拷贝,原型上的属性如何拷贝,这些我都会慢慢研究哒!大家也可以思考一下~
最后,欢迎点赞和收藏!!错误之处欢迎指正(`・ω・´)

您可能感兴趣的文章:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/4f5264505a6c6d2157d296a9fbbb7706.html