如何轻松掌握深浅拷贝:一文看懂!

212 阅读4分钟

浅拷贝:浅拷贝只复制对象的第一层属性。对于原始类型(如字符串、数字、布尔值),拷贝的就是基本类型的值,如果属性是引用类型,拷贝的是内存地址 。

深拷贝:创建一个新的指针 生成一个新的内存地址 这个新指针指向新的内存地址 与原对象相互独立 互不影响

image.png

一、浅拷贝的实现方法:

1.Object.assign()

    const original = { a: 1, b: { c: 2 } };
    const shallowCopy = Object.assign({}, original)
    console.log(shallowCopy);
    // 修改嵌套对象
    shallowCopy.b.c = 3;
    shallowCopy.a = 3;
    console.log(original, shallowCopy); // 3 (原对象也受到影响)
    

image.png

有很多人认为既然是浅拷贝 那为什么修改shallowCopy.a 元对象不受影响呢?

Object.assign({}, original) 创建了一个新的对象 shallowCopy,它复制了 original 的第一层属性。在这个例子中,a 是基本类型(数字),其值被复制到 shallowCopy 中。

修改行为:

当你执行 shallowCopy.a = 3 时,实际上是在 shallowCopy 中创建了一个新的 a 属性的值,不会影响到 original 中的 a。因此,这种情况依然属于 浅拷贝,但由于没有嵌套对象的引用,原对象并未受到影响。original.a 仍然是 1,而 shallowCopy.a 被修改为 3。

总结:在这个示例中,尽管使用了浅拷贝,但由于 a 是基本类型,修改 shallowCopy.a 不会影响 original.a。所以最后的输出是: original 的值仍然是 { a: 1 }; shallowCopy 的值变为 { a: 3 }

2.使用展开运算符

    const original = { a: 1, b: { c: 2 } };
    const shallowCopy = { ...original };
    // 修改嵌套对象
    shallowCopy.b.c = 3;
    console.log(original, shallowCopy); // 3 (原对象也受到影响)
    

image.png

3.使用 Array.prototype.slice() (仅对数组有效)

    const originalArray = [1, 2, { a: 3 }];
    const shallowCopyArray = originalArray.slice();
    shallowCopyArray[2].a = 4;
    console.log(originalArray, shallowCopyArray); // 4 (原数组也受到影响)
    

image.png

4.使用 Array.from() (仅对数组有效)

    const originalArray = [1, 2, { a: 3 }];
    const shallowCopyArray = Array.from(originalArray);
    shallowCopyArray[2].a = 4;
    console.log(originalArray, shallowCopyArray); // 4 (原数组也受到影响)
    

image.png

二、深拷贝的实现方法:

1.JSON.parse(JSON.stringify(obj))

    let a = { a: 1, b: 2 }
    let b = JSON.parse(JSON.stringify(a))
    a.a = 11
    console.log(a, b)//{a:1,b:2}
    

image.png

1.1 JSON.parse(JSON.stringify(obj))深浅拷贝的缺陷

注意:这种方法不能处理函数、undefined、Symbol、日期、正则表达式、循环引用等特殊类型。
    let a = {
        name: 'Jack',
        age: 18,
        hobbit: ['sing', { type: 'sports', value: 'run' }],
        score: {
            math: 'A',
        },
        run: function () { },
        walk: undefined,
        fly: NaN,
        cy: null,
        date: new Date()
    }
    let b = JSON.parse(JSON.stringify(a))
    console.log(b)
    

image.png

2.递归函数

        function deepClone(target) {
            // WeakMap作为记录对象Hash表(用于防止循环引用)
            const map = new WeakMap()

            // 判断是否为object类型的辅助函数,减少重复代码
            function isObject(target) {
                return (typeof target === 'object' && target) || typeof target === 'function'
            }

            function clone(data) {

                // 基础类型直接返回值
                if (!isObject(data)) {
                    return data
                }

                // 日期或者正则对象则直接构造一个新的对象返回
                if ([Date, RegExp].includes(data.constructor)) {
                    return new data.constructor(data)
                }

                // 处理函数对象
                if (typeof data === 'function') {
                    return new Function('return ' + data.toString())()
                }

                // 如果该对象已存在,则直接返回该对象
                const exist = map.get(data)
                if (exist) {
                    return exist
                }

                // 处理Map对象
                if (data instanceof Map) {
                    const result = new Map()
                    map.set(data, result)
                    data.forEach((val, key) => {
                        // 注意:map中的值为object的话也得深拷贝
                        if (isObject(val)) {
                            result.set(key, clone(val))
                        } else {
                            result.set(key, val)
                        }
                    })
                    return result
                }

                // 处理Set对象
                if (data instanceof Set) {
                    const result = new Set()
                    map.set(data, result)
                    data.forEach(val => {
                        // 注意:set中的值为object的话也得深拷贝
                        if (isObject(val)) {
                            result.add(clone(val))
                        } else {
                            result.add(val)
                        }
                    })
                    return result
                }

                // 收集键名(考虑了以Symbol作为key以及不可枚举的属性)
                const keys = Reflect.ownKeys(data)
                // 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性以及对应的属性描述
                const allDesc = Object.getOwnPropertyDescriptors(data)
                // 结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链, 这里得到的result是对data的浅拷贝
                const result = Object.create(Object.getPrototypeOf(data), allDesc)

                // 新对象加入到map中,进行记录
                map.set(data, result)

                // Object.create()是浅拷贝,所以要判断并递归执行深拷贝
                keys.forEach(key => {
                    const val = data[key]
                    if (isObject(val)) {
                        // 属性值为 对象类型 或 函数对象 的话也需要进行深拷贝
                        result[key] = clone(val)
                    } else {
                        result[key] = val
                    }
                })
                return result
            }

            return clone(target)
        }
        let obj = {
            name: 'Jack',
            age: 18,
            hobbit: ['sing', { type: 'sports', value: 'run' }],
            score: {
                math: 'A',
            },
            run: function () { },
            walk: undefined,
            fly: NaN,
            cy: null,
            date: new Date(),
            reg: new RegExp('/我是一个正则/'),
            map: new Map().set('mapKey', 1),
            set: new Set().add('set'),
            [Symbol('10')]: 1
        }
      
        const clonedObj = deepClone(obj)
        obj.age = 26
        obj.hobbit[0] = "2"
        obj.score.math = '88'

        console.log(obj, clonedObj)
        

image.png

3.structuredClone()(现代 API)

        let obj = { a: 1, b: { c: 2 }, d: [3, 4], e: new Date(), f: () => { } };
        let deepCopy = structuredClone(obj);
        deepCopy.b.c = 100;
        deepCopy.d[0] = 300;
        console.log(deepCopy)
        

image.png

缺陷:这种方法不能处理函数 报错:深浅拷贝.html:88 Uncaught DOMException: Failed to execute 'structuredClone' on 'Window'