浅拷贝与深拷贝

316 阅读5分钟

一、数据类型

数据类型分为基本数据类型(String、Number、Boolean、Number、Null、Undefined、Symbol)和复杂数据类型。

  1. 基本数据类型的特点是数据直接存储在栈中。

  2. 复杂数据类型的特点是栈中存储的是对象的地址(指针),真实的数据存储在堆内存中,通过栈中存储的指针来获取到指向的堆内存中的数据。

    简单如图所示:

二、浅拷贝

浅拷贝:浅拷贝拷贝时会在堆中创建一个新的内存来保存原始对象的数据,当原始对象中只有简单数据类型的时候,拷贝的其实就 是基本数据类型的值,这时会给人造成一种迷惑看似是深拷贝。但当原始对象中有复杂数据类型时,只能拷贝原始对象内的复杂数据类型的指针去指向原始对象的复杂数据类型在堆中的空间内的数据。

小结:浅拷贝就是只能拷贝单一层级的对象,无法拷贝多层级别的对象。

实现浅拷贝的几种方式(如有缺失,欢迎补充....)

1、Object.assign()方法
let obj = {
            person: {
                name: 'zs',
                age: 18
            },
            sports: 'soccer'
        };
        let obj1 = Object.assign({}, obj);
        // console.log(obj1);
        obj1.person.age = 22; // 二级属性实现的时浅拷贝
        obj1.sports = 'baseball'; // 一级属性实现的时深拷贝
        console.log(obj);

运行结果:

上述实例通过Object.assign方法拷贝了一个多层级的复杂数据类型,通过拷贝的对象修改对象中的sports属性和二级对象中的age属性,运行结果可以清晰的看见原始对象中一级属性sports值没有改变,而二级对象中的age属性改变了。

2、展开运算符实现浅拷贝
let obj = {
            person: {
                name: 'zs',
                age: 18
            },
            sports: 'soccer'
        };
        let obj1 = {
            ...obj
        };
        obj1.person.age = 22; // 浅拷贝
        obj1.sports = 'baseball'; //深拷贝
        console.log('obj1', obj);

运行结果:

通过运行结果能够清晰的得到与Object.assign方法一样的结果:浅拷贝只能实现一级对象的深拷贝,当原始对象内有复杂数据类型是只能拷贝复杂数据类型的地址引用。

3、函数库lodash的_.clone方法(目前了解不多,就不误人子弟去演示了)

接下来看两中数组的浅拷贝

4、Array.concat()方法
let arr = [1, 3, {
            username: 'zs'
        }];
        let arr1 = arr.concat();
        arr1[0] = 2222;
        arr1[2].username = '小葱';
        console.log('arr', arr);

运行结果:

由此可知数组的拷贝也是和上述结论一样的,在这里就不做多赘述。

5、Array.slice()方法
 let arr = [1, 3, {
            username: 'zs'
        }];
        let arr1 = arr.slice();
        arr1[0] = 2222;
        arr1[2].username = '小葱';
        console.log('arr', arr);

运行结果:

浅拷贝小结:经过上述的 '实践是检验真理的唯一标准' 我们可以知道 浅拷贝就是当原始对象中有复杂数据类型是只能拷贝复杂数据类型的指针,无法完整的将复杂数据类型的数据拷贝一份

三、深拷贝

深拷贝:深拷贝就是拷贝原始对象的所有层级,不管原始对象中存在的是何种数据类型数据,都会将数据完整的复制一份,放入新开辟的空间,并且赋予新的指针,和原始对象互不干扰。

实现深拷贝的几种方式(如有缺失,欢迎补充....)

1、JSON.parse(JSON.stringify())方法
let arr = [1, 3, {
            username: 'zs'
        }, function say() {
            console.log('你好');
        }];
        let arr1 = JSON.parse(JSON.stringify(arr));
        arr1[1] = 2222;
        arr1[2].username = '小葱';
        console.log(arr);

运行结果:

通过运行结果可以清晰的看到:通过拷贝后的arr1去修改数据,原始对象的数据完全不受影响,但要注意的是 拷贝后对象内的函数消失了

**注意:**JSON.parse(JSON.stringify())方法,虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。

2、 函数库lodash的_.cloneDeep方法(本人了解的也不多,就不多做赘述了..才疏学浅..定当加倍努力!!!)
3、jQuery.extend()方法
 var obj = {
            a: 1,
            b: {
                f: {
                    g: 1
                }
            },
            c: [1, 2, 3]
        };
        var obj1 = $.extend(true, {}, obj);
        console.log(obj.b.f === obj1.b.f); // false
4、手写递归方法实现深拷贝

****递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝

// 注:只是一个简单的基本实现展示,并不是最佳实践
        function deepCopy(obj) {
            let objClone = Array.isArray(obj) ? [] : {};
            // 如果是对象循环对象
            if (obj && typeof obj === 'object') {
                // 循环遍历对象
                for (attr in obj) {
                    // 判断obj子元素是否是对象,如果是,进行递归复制
                    if (obj[attr] && typeof obj[attr] === 'object') {
                        objClone[attr] = deepCopy(obj[attr]);
                    } else {
                        objClone[attr] = obj[attr];
                    };
                };
            };
            return objClone;
        };
        let a = [1, 2, [5, 7], 5];
        b = deepCopy(a);
        b[1] = 2222;
        b[2][0] = 333;
        console.log(a); // 不变

运行结果:

上述的手写递归方法虽然能够实现深拷贝,但是要注意的是:这只是一个简单的实现,并不是最佳实现。

四、结束演讲!

一入前端深似海,从此头发是路人的,妹子也是路人的......我们只有不停的学习专研才能够支撑的起植发的钱.....

言归正传...简而言之:深拷贝就是拷贝原始对象的所有层级,而浅拷贝只能拷贝单一层级!!!

​ 其中最后演示的手写递归方法实现深拷贝只是简单的功能实现,并不严谨,只有不断地专研学习才能是代码更加严谨、完美、漂亮、让人赞叹、令人钦佩、使我们自豪。生命不止学习不止......