zoukankan      html  css  js  c++  java
  • js深拷贝总结

    JS深拷贝总结

    JS的原生方法不支持深拷贝,Object.assign{...obj}都属于浅拷贝,下面我们讲解如何使用JS实现深拷贝。

    JSON.sringify 和 JSON.parse 

    这是JS实现深拷贝最简单的方法了,原理就是先将对象转换为字符串,再通过JSON.parse重新建立一个对象。 但是这种方法的局限也很多:

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

    我们依次看看这三点,我们测试一下这段代码:

    let obj = {         
        reg : /^asd$/,
        fun: function(){},
        syb:Symbol('foo'),
        asd:'asd'
    }; 
    let cp = JSON.parse(JSON.stringify(obj));
    console.log(cp);

    结果:

    可以看到,函数、正则、Symbol都没有被正确的复制。

    如果在JSON.stringify中传入一个循环引用的对象,那么会直接报错:

    在说第三点之前,我们看看这段代码:

    let obj = {  asd:'asd' }; 
    let obj2 = {name:'aaaaa'};
    obj.ttt1 = obj2;
    obj.ttt2 = obj2;
    let cp = JSON.parse(JSON.stringify(obj)); 
    obj.ttt1.name = 'change'; 
    cp.ttt1.name  = 'change';
    console.log(obj,cp);

    在原对象 obj 中的 ttt1 和 ttt2 指向了同一个对象 obj2,那么我在深拷贝的时候,就应该只拷贝一次 obj2 ,下面我们看看运行结果:

    我们可以看到(上面的为原对象,下面的为复制对象),原对象改变 ttt1.name 也会改变 ttt2.name ,因为他们指向相同的对象。

    但是,复制的对象中,ttt1 和 ttt2 分别指向了两个对象。复制对象没有保持和原对象一样的结构。因此,JSON实现深复制不能处理指向相同引用的情况,相同的引用会被重复复制。

    递归实现

    JS原生的方法不能很好的实现深复制,那么我们就动手实现一个。

    思想非常简单:对于简单类型,直接复制。对于引用类型,递归复制它的每一个属性。

    我们需要解决的问题:

    • 循环引用
    • 相同引用
    • 不同的类型(笔者仅实现了数组和对象的区分)

    实现代码:

    function deepCopy(target){ 
    let copyed_objs = [];//此数组解决了循环引用和相同引用的问题,它存放已经递归到的目标对象 
        function _deepCopy(target){ 
            if((typeof target !== 'object')||!target){return target;}
            for(let i= 0 ;i<copyed_objs.length;i++){
                if(copyed_objs[i].target === target){
                    return copyed_objs[i].copyTarget;
                }
            }
            let obj = {};
            if(Array.isArray(target)){
                obj = [];//处理target是数组的情况 
            }
            copyed_objs.push({target:target,copyTarget:obj}) 
            Object.keys(target).forEach(key=>{ 
                if(obj[key]){ return;} 
                obj[key] = _deepCopy(target[key]);
            }); 
            return obj;
        } 
        return _deepCopy(target);
    }

    copyed_objs 这个数组存放的是已经递归过的目标对象。在递归一个目标对象之前,我们应该检查这个数组,如果当前目标对象和 copyed_objs 中的某个对象相等,那么不对其递归。

    这样就解决了循环引用和相同引用的问题。

    测试一下代码:

    var a = {
        arr:[1,2,3,{key:'123'}],//数组测试
    };
    a.self = a;//循环引用测试
    a.common1 = {name:'ccc'};
    a.common2 = a.common1;//相同引用测试
    var c = deepCopy(a);
    c.common1.name = 'changed';
    console.log(c);

    结果:

    over

    原文:https://juejin.im/post/5b20c9f65188257d7d719c1c

  • 相关阅读:
    线程的实现方式
    实现一个拷贝文件的工具类,要使用字符流还是字节流
    String&&StringBuilder&&StringBuffer
    面向对象的特征
    索引的选择
    TCP之Nagle算法&&延迟ACK
    通用套接字选项和TCP套接字选项
    TCP之非阻塞connect和accept
    TCP之种种连接异常
    TCP之listen&backlog
  • 原文地址:https://www.cnblogs.com/djjlovedjj/p/11202139.html
Copyright © 2011-2022 走看看