zoukankan      html  css  js  c++  java
  • 我理解的浅拷贝和深拷贝

    浅拷贝:通过一个对象来创建一个新对象,如果对象的属性的值是基本数据类型,直接把值赋给新对象,如果值是引用类型则把数据的对应的内存地址赋给新对象;因为两个对象对应的是同一个内存地址,当其中一个对象数据改变了,就相当于改变了内存地址,所以会影响到别一个对象;

     

    深拷贝:通过一个对象来创建一个新对象,无论基本类型还是引用类型都将值赋给新对象,引用类型会开辟一块新的内存地址用于存放数据,因为不会相互影响;

     

     浅拷贝的方法:

     

     1.直接赋值:

     var obj1 = {a: 1, b: 2};
    
     var obj2 = obj1;
    
     obj2.b = 22;
    
     console.log(obj1.b); // 22

     

     2.Object.assign

    var obj1 = {a: 1, b: 2};
    var obj2 = {c: 3};
    Object.assign(obj1, obj2);
    console.log(obj1) // {a: 1, b: 2, c: 3}
    obj2.c = 4;
    console.log(obj1.c) // 3

    乍一看,这貌似两个对象互不影响了!难道是深拷贝了?

    var obj1 = {a: 1, b: 2};
    var obj2 = {c: {z: 3}};
    Object.assign(obj1, obj2);
    console.log(obj1); // {a: 1, b: 2, c: {z: 3}}
    console.log(obj2); // {c: {z: 3}}
    obj2.c.z = 4;
    console.log(obj1.c.z); // 4;

    由些可见,Object.assign只是对第一层数据进行了深拷贝,实际上依然是一个浅拷贝。

     

     3.通过对象扩展运算符 ({ ...obj })

    var obj1 = {
        a: 1,
        b: 2,
        c: {
            z: 3
        }
    };
    var obj2 = {...obj1};
    console.log(obj2); // { a: 1, b: 2, c: { z: 3 } }
    
    obj1.a = 11;
    console.log(obj2.a);  // 1
    
    obj1.c.z = 33;
    console.log(obj2.c.z); // 33

    由此可见...扩展运算符跟assign一样也是浅拷贝。

     

     4.通过数组的slice方法 

     

    var arr1 = [1, [2, 3], 4];
    var arr2 = arr1.slice();
    
    console.log(arr2); // [1, [2, 3], 4]
    
    arr1[0] = 11;
    console.log(arr2[0]); // 1; 貌似深拷贝了,但是:
    
    arr1[1][0] = 22;
    console.log(arr2); // [1, [22, 3], 4;  还是太年轻啊!!!

     

     5.通过数组的concat方法。

     

    var arr1 = [1, [2, 3], 4];
    var arr2 = arr1.concat();
    
    console.log(arr2); //  [1, [2, 3], 4]
    
    arr1[0] = 11;
    console.log(arr2[0]); // 1;
    
    arr1[1][0] = 22; 
    console.log(arr2[1][0]); // 22

    深拷贝的方法:

    1. JSON.stringify() + JSON.parse();

    原理是:把一个对象序列化成为一个JSON字符串,将对象的内容转换成字符串的形式再保存在磁盘上,再用JSON.parse()反序列化将JSON字符串变成一个新的对象;

    var obj1 = {
        a: 1,
        b: 2,
        c: {
            z: 3
        }
    };
    
    var obj2 = JSON.parse(JSON.stringify(obj1));
    console.log(obj2); // {a: 1, b: 2, c: {z: 3}}
    obj1.c.z = 33; 
    console.log(obj2.c.z);
    // 3

    由此可见,已经实现了深拷贝!

    但是,JSON.stringify()有个问题:无法拷贝函数、undefined、Symbol值,因为其在序列化时会忽略这些值;

    var obj1 = {
        a: 1,
        b: 'b',
        c: [1,2,3],
        u: undefined,
        s: Symbol('s'),
        f: function() {
            console.log(123);
        }
    };
    
    var obj2 = JSON.parse(JSON.stringify(obj1));
    
    console.log(obj2); // {a: 1, b: 'b', c: [1, 2, 3]}

    2.递归:

    var deepCopy = function(obj) {
        if (typeof obj !== 'object') return;
        var newObj = obj instanceof Array ? [] : {};
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
            }
        }
        return newObj;
    }

    var obj1 = {
        a: 1,
        b: 'b',
        c: [1,2,3],
        u: undefined,
        s: Symbol('s'),
        f: function() {
            console.log(123);
        }
    };

    var obj2 = deepCopy(obj1)

     

    哇~ 完美!!! 感觉得到了整个世界呢!

    但是:

    let y = {
      a: "z",
      [Symbol("z")]: "d"
    };
    
    let newY = deepCopy(y);
    
    console.log(newY) // --> {a: "z"}
    [Symbol("z")]: "d" 呢?

    原来 for..in 是无法获取Symbol作为健名的元素的! 那该怎么办呢? 我们可以利用
    Reflect.ownKeys()来得到包括Symbol在内所有实例的属性,从而可以实现Symbol属性的深拷贝!!!
    /**
     * S.工具函数
     * */
    // 判断是否是对象(函数和object,不包括null)
    function isObject(obj) {
        var type = typeof obj;
        return type === 'function' || type === 'object' && !!obj;
    }
    // Internal function for creating a toString-based type tester. (underscore判断数据类型[object Array] 和 [object Object])- tagTester('Object')
    function tagTester(name) {
        return function(obj) {
            return toString.call(obj) === '[object ' + name + ']';
        };
    }
    // 判断是否为数据(原生支持)
    let isArray = Array.isArray;
    /**
     * E.工具函数
     * */
    // 深拷贝
    function deepClone(obj) {
        let objIsObject = tagTester('Object')(obj);
        let objIsArray = isArray(obj);
        if (!objIsObject && !objIsArray) return;
        let newObj = objIsArray ? [] : {};
        if (objIsArray) {
            // 如果是数组...
            return obj.map((item) => {
                return (tagTester('Object')(item) || isArray(item)) ? deepClone(item) : item;
            })
        }
        if (objIsObject) {
            // 获取对象的所以key(包括symbol的key)
            let ownKeys = Reflect.ownKeys(obj);
            for (let i = 0, Len = ownKeys.length; i < Len; i++) {
                let key = ownKeys[i];
                let curObj = obj[key];
                if (tagTester('Object')(curObj) || isArray(curObj)) {
                    newObj[key] = deepClone(curObj);
                } else {
                    newObj[key] = curObj
                }
            }
        }
        return newObj;
    }
    
    // test
    let obj = {
        num: 1,
        str: 'string',
        nu: null,
        un: undefined,
        fn: function(a) { 
            a += 1;
            return a;
        },
        ob: {
            x: 1,
            y: 2,
            z: {
                a: 'a',
                b: 'b'
            }
        },
        [Symbol('foo')]: 'i am symbol',
        arr: [1, 2, 3, [4, 5], {m: 9, n: 8}, function() {return 1}]
    };
    var newObj = deepClone(obj);

       这样一来就真的得到了整个世界了!

  • 相关阅读:
    基于遗传算法(Genetic Algorithm)的TSP问题求解(C)
    分治思想:合并排序和快速排序
    冒泡排序和选择排序
    WPF线程
    DataGrid属性和事件
    WPF限制TextBox只能输入数字
    键盘键值对应表
    转换人民币大小金额
    查找DataGrid某个单元格中的控件
    WPF中DataGrid使用初步
  • 原文地址:https://www.cnblogs.com/garfieldzhong/p/11986486.html
Copyright © 2011-2022 走看看