js之浅拷贝和深拷贝

132 阅读3分钟

前言

1.1数据类型的复制

基本数据类型的复制,是按值传递的。

    let a = 1;
    let b = a;
    b = 2;
    console.log(a);//1
    console.log(a);//2

引用数据类型的复制,是按引用传值的。

    let obj={
        a:1,
        b:2
    }
    let obj1 = obj;
    obj1.a=3;
    console.log(obj.a);//3
    console.log(obj1.a);//3

1.2深拷贝与浅拷贝

深拷贝浅拷贝都只针对引用数据类型,浅拷贝会对对象逐个成员依次拷贝,但只复制内存地址,而不复制对象本身,新旧对象成员还是共享同一内存;深拷贝会另外创建一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

两者的区别在于:浅拷贝只是简单的复制,对对象里面的对象属性和数组属性只是复制了地址,并没有创建新的相同对象或者数组。而深拷贝是完完全全的复制一份,空间大小占用一样但是位置不同!

浅拷贝

当把一个对象赋值给一个新的变量时,赋的对象是该对象在栈中的地址,而不是堆中的数据。也就是新旧两个对象指的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,两个对象联动的会一起改变。

1.通过一个空对象进行浅拷贝

        let b = {
            name: '小明',
            age: 18,
            home: "天津"
        }
        let c = {};
        function extend(obj1, obj2) {
            for (let item in obj1) {
                // obj1的值赋值给了obj2
                obj2[item] = obj1[item];
            }
        }
        extend(b, c)
        c.name = '小红';
        c.home = '云南';
        console.log(c, b);

浅拷贝1.png

2.原型式浅拷贝

        let obj1 = {
            name: "张三",
            say: 'hello'
        }
        Object.prototype.clone = function () {
            let obj = {};
            for (item in this) {
                obj[item] = this[item];
            }
            return obj;
        }
        let obj2 = obj1.clone();
        console.log(obj2)

浅拷贝2.png

3.Object.assign()方法

    let obj1 = {
            name: "张三",
            say: 'hello'
        }
    let obj3 = Object.assign({}, obj1);
        console.log(obj3)

浅拷贝3.png

4.通过三点的方法

        let state = {
            nickname: '李四',
            token: "abcdefg"
        }
        console.log({ ...state });

浅拷贝4.png

5.concat()和slice()

concat()和slice()均为Array原型上的方法,只适用于Array。

        let arr1 = [1, 2, { name: '张三' }];
        let arr2 = arr1.concat();
        arr2[0] = 3;
        arr2[2].name = '李四';
        console.log('arr1', arr1)
        console.log('arr2', arr2)

        let arr1 = [1, 2, { name: '张三' }];
        let arr2 = arr1.slice();
        arr2[0] = 3;
        arr2[2].name = '李四';
        console.log('arr1', arr1)
        console.log('arr2', arr2)

浅拷贝5.png

补充说明:Array的slice和contact方法都不会修改原数组,而是会返回一个对原数组进行浅拷贝的新数组。这两种方法同Object.assign()一样,都是对第一层属性依次拷贝,如果第一层的属性是基本数据类型,就拷贝值;如果是引用数据类型,就拷贝内存地址。

深拷贝

把对象的属性中所有引用类型的值,遍历到是基本类型的值为止。

1.JSON.parse(JSON.stringify())方法

JSON.stringify() 将json转换为string类型 可以将前端需要传到后端数据字符串化,JSON.parse() 将json字符串转换为json对象 将后传入到前端的字符串json 对象化

        let obj = {
            'id': 1,
            'name': "杰克",
            'msg': {
                'age': 10,
                'name': '露丝',
                'message': {
                    'id': 10001,
                    'name': "王五"
                }
            },
            'color': ['red', 'blue', 'green']
        }
        let obj1 = JSON.parse(JSON.stringify(obj));
        obj1.name = "李四";
        obj1.msg.age = 20;
        obj1.msg.message.id = 10010;
        console.log("obj", obj)
        console.log("obj1", obj1)

深拷贝.png

新对象下的数据改变不影响原对象。

缺点:这种方法可以实现数组和对象和基本数据类型的深拷贝,但不能处理函数。因为JSON.stringify()方法是将一个javascript值转换我一个JSON字符串,不能接受函数。

2.通过递归实现深拷贝

        let obj = {
            'id': 1,
            'name': "杰克",
            'msg': {
                'age': 10,
                'name': '露丝',
                'message': {
                    'id': 10001,
                    'name': "王五"
                }
            },
            'color': ['red', 'blue', 'green']
        }
        // 递归实现
        let obj1 = {}
        function fun(newObj, oldObj) {
            for (let key in oldObj) {
                let item = oldObj[key];
                if (item instanceof Array) {
                    newObj[key] = [];
                    fun(newObj[key], item)
                } else if (item instanceof Object) {
                    newObj[key] = {};
                    fun(newObj[key], item);
                } else {
                    newObj[key] = item;
                }
            }
        }
        fun(obj1, obj);
        console.log(obj1)

深拷贝1.png