简单的深拷贝和浅拷贝

66 阅读2分钟

js的数据存储方式

  • 基本数据类型:变量名(a)和变量值(1) 存储在栈中
  • 复杂数据类型:变量名(b),存储在栈中;变量值({name: 'lihua'}),存储在堆中

赋值

首先说明这里说的赋值,不是直接把引用值(例如:{})或者原始值(例如:false、1、"str"等)直接赋值给一个变量。而是通过变量把一个值赋值给另一个变量。

深拷贝和浅拷贝

  • 浅拷贝是什么?

浅拷贝是创建一个对象,这个对象有原始对象属性值的一份精确拷贝。 如果属性是基本类型,拷贝的就是基本类型的值; 如果属性时引用类型,拷贝的就是内存地址。

        let obj = {
            name: 'll',
            hobby: ['买花', '逛街'],
        };
        // 方法1: let obj1 = {...obj}
        // 方法2: let obj1 = Object.assign({}, obj);

        // 方法3:递归
        function shallCopy(source) {
            // 突出一个『新』对象
            let obj = {};
            for (let i in source) {
                if (source.hasOwnProperty(i)) {
                    obj[i] = source[i];
                }
            }
            return obj;
        }
        let obj1 = shallCopy(obj);
        obj.name = 'emma';
        // hobby是引用数据类型
        obj.hobby[0] = '吃饭';
        console.log(obj);
        /**
         * {
         *      name: "emma",
         *      hobby: ['吃饭', '逛街']
         * }
        */
        console.log(obj1);
        /**
         * {
         *      name: "ll",
         *      hobby: ['买花', '逛街']
         * }
        */
        
        // 注:直接修改 obj.hobby = {}, obj1的hobby不会变,相当于直接修改源,而不是引用类型
  • 深拷贝是什么?

深拷贝拷贝是创建一个对象,深拷贝是将一个对象从内存中完整的拷贝份出来,从堆内存中开辟一个新的区域存放新对象,彼此完全独立。 如果属性是基本类型,拷贝的就是基本类型的值; 如果属性是引用类型,贝的不是内存地址而是要从堆内存中开辟一个新的区域存放新对象, 彼此完全独立。

    let obj = {
        name: 'll',
        hobby: ['买花', '逛街'],
        // JSON拷贝,无法处理
        date: new Date(),
    };

    // 方法1:
    // 使用JSON.parse(JSON.stringify(xxx))
    let deepObj = JSON.parse(JSON.stringify(obj))
    // 注:没法对函数,日期,正则那些处理
    obj.name = 'emma';
    obj.hobby[0] = '吃饭';
    console.log(obj);
    /**
     * {
     *      name: "emma",
     *      hobby: ['吃饭', '逛街']
     * }
    */
    console.log(deepObj);
    /**
     * {
     *      name: "ll",
     *      hobby: ['买花', '逛街']
     * }
    */

    // 方法2:  
    // 使用递归方法(https://juejin.cn/post/7075351322014253064)
    function deepCopy(source) {
        if (typeof source === null) {
            return source;
        }

        if (source instanceof Date) {
            return new Date(source);
        }
        if (source instanceof RegExp) {
            return new RegExp(source);
        }
        // ... (类型)判断
        if (typeof source !== 'object') {
            return source;
        }
        let obj = Array.isArray(source) ? [] : {};
        for (let i in source) {
            if (source.hasOwnProperty(i)) {
                obj[i] = deepCopy(source[i]);
            }
        }
        return obj;
    }

    // 方法3: lodash中的cloneDeep
    // 方法4:structuredClone(value),有兼容问题

注意

  • 赋值操作符是把一个对象的引用赋值给一个变量,所以变量中存储的是对象的引用。

  • 浅复制是复制源对象的每个属性,但如果属性值是对象,那么复制的是这个对象的引用。所以源对象和拷贝对象之间共享嵌套对象。

  • 深复制与浅复制不同的地方在于,如果属性值为对象,那么会复制该对象。源对象和拷贝对象之间不存在共享的内容。