关于js浅拷贝和深拷贝例子和方法解析(概念 区别 方法 总结 使用)

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
            /*浅拷贝和深拷贝的概念和区别
                浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,
                新旧对象还是共享同一块内存;但深拷贝会另外创造一个一模一样的对象,
                新对象跟原对象不共享内存,修改新对象不会改到原对象;
            区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制;
            */
           
            // 第一种方式:for···in只循环第一层 浅拷贝的方法
            // 只复制第一层的浅拷贝
            function simpleCopy(obj) {
               let newObj = Array.isArray(obj) ? [] : {};
               for (let i in obj) {
               newObj[i] = obj[i];
              }
               return newObj;
            }
            
            let testobj = {
               a: 1,
               b: 2,
               c: {
                     d: 3
                  }
            }
            let testobj2 = simpleCopy(testobj);
            testobj2.a = 3;
            testobj2.c.d = 4;
            // 单层
            console.log(testobj.a); // 1
            console.log(testobj2.a); // 3
            // 多层
            console.log(testobj.c.d); // 4
            console.log(testobj2.c.d); // 4
            
            // 第二种方式Object.assign方法浅拷贝一层
            // 第三种方法 = 赋值 解构赋值

            // 第一种方式:(递归拷贝) 深拷贝  WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的
            // WeakMap 的 key 只能是 Object 类型。 原始数据类型 是不能作为 key 的(比如 Symbol)
            function deepClone(obj, hash = new WeakMap) {
                // 先把特殊情况全部过滤 null undefined date reg
                if (obj == null) return obj
                if (typeof obj !== 'object') return obj
                if (obj instanceof Date) return new Date()
                if (obj instanceof RegExp) return new RegExp(obj)
                // 判断 [] {} 是对象还是数组 typeof instanceof constructor
                // 有拷贝后的直接返回
                if (hash.has(obj)) {
                    return hash.get(obj) // 解决循环引用的问题
                }
                // new 的实现原理
                let instance = new obj.constructor
                // 制作一个映射表
                hash.set(obj, instance)
                // 把实例上的属性拷贝到这个对象身上,把原型链指向到原型对象上
                for (let key in obj) {
                    // 不拷贝原型链上的属性
                    if (obj.hasOwnProperty(key)) {
                        instance[key] = deepClone(obj[key], hash)
                    }
                }
                return instance
            }
            /************上面是封装的深拷贝函数,下面是测试代码包含多种情况************/
            let obj = {}
            obj.a = 1
            obj.name = 'bob'
            console.log(deepClone(obj), 'obj') // {a: 1, name: "bob"} "obj"

            let obj2 = {}
            obj2.b = obj2
            let aa = {
                age: 18,
                name: obj2
            }
            console.log(deepClone(aa), '深度拷贝')

            let arr = [1, 2, 3, 4, undefined, null]
            console.log(deepClone(arr), 'arr') // [1, 2, 3, 4, undefined, null] "arr"

            let fuzaobj = {
                a: {
                    b: null,
                    c: /a/,
                    d: undefined
                },
                b: function() {
                    console.log(this.a)
                },
                c: [{
                        a: 'c',
                        b: /b/,
                        c: undefined
                    },
                    'a',
                    3
                ]
            }
            console.log(deepClone(fuzaobj), '复杂对象拷贝')


            // 第二种方式:简单版本深拷贝 JSON.parse(JSON.stringify(obj)) 
            function deepCloneSimple(obj) {
                return JSON.parse(JSON.stringify(obj))
            }

            let test = {
                a: 11,
                name: 'pop'
            }
            let deeptest = deepCloneSimple(test)
            deeptest.a = 5;
            console.log(test.a); // 11
            console.log(deeptest.a); // 5

            let test2 = {
                a: undefined,
                b: /b/,
                c: null,
                d: 111,
                e: 'ssrrs',
                f: {
                    name: 'bob'
                },
                h: function() {
                    console.log(111)
                }
            }
            console.log(deepCloneSimple(test2))
            // {
            //     b: {}
            //     c: null
            //     d: 111
            //     e: "ssrrs"
            //     f: {
            //         name: "bob"
            //     }
            // }

            // 第三种方式拷贝:Object.assign(target, source)
            function deepOnlyOneClone(obj) {
                return Object.assign({}, obj);
            }

            let test3 = {
                a: 1,
                b: 2,
                c: [1, 2, 3]
            }
            let test4 = Object.assign({}, test3);
            test4.c[1] = 5;
            console.log(test3.c); // [1, 5, 3]
            console.log(test4.c); // [1, 5, 3]

            let test5 = {
                a: null,
                b: /bbb/,
                c: undefined,
                d: function() {
                    console.log(111)
                },
                e: {
                    name: 'bob',
                    age: 10,
                    desc: {
                        pre: '1111',
                        qto: 'ahhha'
                    }
                },
                f: [1, 2, 3],
                g: {
                    h: {
                        k: 100
                    }
                }
            }
            let test6 = deepOnlyOneClone(test5)
            test5.a = 111;
            test6.a = 333;
            // 单层测试
            console.log(test5.a) // 111
            console.log(test6.a) // 333
            // 多层测试
            test5.e.name = 'piter';
            test6.e.name = 'sos';
            console.log(test5.e.name) // sos
            console.log(test6.e.name) // sos
            // 完整结构测试
            console.log(deepOnlyOneClone(test5), '只能拷贝一层')
            
            // 结论
            // 函数没必要拷贝因为函数拷贝还是函数
            // Object.assign(target, source) 当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝
            // 使用JSON.stringify()以及JSON.parse()它是不可以拷贝undefined,function,RegExp 等等类型的
            // 深拷贝作用在引用类型上!例如:Object,Array
            // 深拷贝不会拷贝引用类型的引用,而是将引用类型的值全部拷贝一份,形成一个新的引用类型,这样就不会发生引用错乱的问题
            // 使得我们可以多次使用同样的数据,而不用担心数据之间会起冲突
            // 项目如需用到可以借助第三方库(lodash.cloneDeep)或者用递归拷贝,
        </script>
    </body>
</html>

 

浅拷贝:是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象 or
重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

 

浅拷贝的实现方式:Object.assign()、函数库lodash的_.clone()、展开运算符..、Array.prototype.slice()、Array.prototype.concat()
例如:Object.assign({}, obj)、_.clone(obj)、let newObj= {... obj1}、arr.concat()、arr.slice();

 

深拷贝:是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
or 从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。

 

深拷贝的实现方式:JSON.parse(JSON.stringify())、函数库lodash的_.cloneDeep方法、jQuery.extend()方法[$.extend(true, {}, obj)]、手写递归方法(递归拷贝 + WeakMap)

 

代码整理总结,转载请注明出处谢谢合作!

posted @ 2020-06-15 11:48  鱼樱前端  阅读(292)  评论(0编辑  收藏  举报