I
M
A
G
I
N
E

js深拷贝总结

JSON.stringify && JSON.parse

这是最简单的js实现深拷贝方式了,原理是先将对象转换为字符串,再通过JSON.parse重新建立一个对象。

但这种方式存在一定的局限性:

  • 不能复制Function、正则、Symbol
  • 循环引用会报错
  • 相同引用会被重复复制

根据以上三点我们来验证,实测代码如下:

1    let obj = {
2     func: function() {},
3     reg: /^abc$/,
4     syb: Symbol('demo'),
5     str: 'abc'
6   }
7 
8   let copyObj = JSON.parse(JSON.stringify(obj))
9   console.log(copyObj)

打印结果:Function、正则和Symbol都没有被复制正确的值

如果JSON.stringify中传入一个循环引用的对象,就会报错:

我们来看看下面这段代码:

 1  let obj1 = {name:'Mike'}
 2  let obj2 = {age:18}
 3  
 4  obj1.text1 = obj2   // {age:18}
 5  obj1.text2 = obj2   // {age:18}
 6  
 7  let copys = JSON.parse(JSON.stringify(obj1))
 8  
 9  obj1.text1.age = 22
10  copys.text1.age = 22
11  
12  console.log('原对象',obj1)
13  console.log('复制对象',copys)

结果:

我们看到当原对象的text1.age改变时text2.age也会改变;因为它们指向相同的对象。

但是,在复制对象中text1和text2分别指向两个对象,复制对象没有保持和原对象一样的结构;所以可以得出:

JSON实现深拷贝不能处理指向相同引用的情况,相同的引用会被重复复制。

 

递归实现深拷贝

通过递归遍历实现深拷贝,对于简单类型,进行直接复制;引用类型,递归复制它的每一个属性。

 1 function isObject(obj) {
 2   return obj !== null && typeof obj === 'object'
 3 }
 4 
 5 function _cloneDeep(target) {
 6   
 7   if(!isObject(target)) return target
 8 
 9   let result = Array.isArray(target) ? [] : {}
10   
11   const keys = Object.keys(target);
12   for(let i = 0, len = keys.length; i < len; i++) {
13     result[keys[i]] = cloneDeep(target[keys[i]])
14   }
15   return result
16 }

以上代码并没有考虑到循环引用和相同引用的问题;对于循环引用的对象使用的话会直接栈溢出,会出现和JSON方法一样的问题。

解决方法和思路:

1、通过闭包维护一个变量,变量中存储已经遍历过的对象

2、每次递归时判断当前的参数是否已经存在于变量中;如果已经存在,说明已经递归过该对象,就停止这次递归并返回上次递归该对象时的返回值

代码实现如下:

 1 function isObject(obj) {  // 判断类型
 2   return obj !== null && typeof obj === 'object'
 3 }
 4 
 5 function _cloneDeep(obj){ 
 6   let visitedObjs = [];
 7   function baseClone(target){
 8 
 9     if(!isObject(target)) return target
10 
11     for(let i = 0; i < visitedObjs.length; i++){
12       if(visitedObjs[i].target === target){        
13         return visitedObjs[i].result;
14       }
15     }
16 
17     let result = Array.isArray(target) ? [] : {} 
18     
19     visitedObjs.push({ target, result }) 
20 
21     const keys = Object.keys(target);
22     for(let i = 0, len = keys.length; i < len; i++) {
23       result[keys[i]] = baseClone(target[keys[i]])
24     }
25     return result
26   } 
27   return baseClone(obj);
28 }

 

posted @ 2021-01-06 16:48  Imagine、  阅读(120)  评论(0编辑  收藏  举报