浅谈深拷贝与浅拷贝

前言

在使用JavaScript中有很多方法可以进行复制对象,但是在使用过程中要千万小心。你可能一不小心就掉入设计好的陷阱之中。
阅读完本文,你将学会如何正确的复制对象

首先我们要了解深/浅拷贝,它们与赋值之间存在着那些区别

浅拷贝

浅拷贝顾名思义就是创建一个新的对象,然后将别人的对象属性值进行一次精准的拷贝,如果属性是基本类型,所拷贝的内容就是基本类型的值。如果拷贝的是引用类型,拷贝的只是内存地址,它与原对象指向同一个内存空间。

深拷贝

深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新的对象不会影响到原对象。

举栗子.jpg

浅拷贝就是和别人共同租房,共用同一片天地,你在客厅吃了一份臭臭的臭豆腐,你的同居室友也会受到影响。

深拷贝就是你看到好友要买房子,房子非常的漂亮,也符合你的要求,而且你还想和他做个邻居,然后你就在他家附近买了一套一模一样户型的房子。

这样大家是不是就很容易理解了。

接下来直接上代码展示

浅拷贝

        let a = {
            name: '漩涡鸣人',
            age: 18
        }
        let b = a;
        console.log('a的结果是', a);
        console.log('b的结果是', b);
        b.name = '宇智波佐助'
        console.log('修改新对象内容之后a的结果是', a);
        console.log('修改新对象内容之后b的结果是', b);
复制代码

浅拷贝代码案例运行结果.png
咦?明明是复制了一份,为什么修改b的属性值,a也跟着变了。

沉思.jpg
原来我们拷贝的是一个对象,它们共同使用同一个存储空间。

        let a = '漩涡鸣人'
        let b = a;
        console.log('a的结果是', a);
        console.log('b的结果是', b);
        b = '宇智波佐助'
        console.log('修改新对象内容之后a的结果是', a);
        console.log('修改新对象内容之后b的结果是', b);
复制代码

浅拷贝数据属性代码案例.png
看,如果我们拷贝的只是一个基本属性,我们对b进行修改将不会影响到a。

①基本类型:直接存储在栈中的数据。(字符串、布尔值、未定义、数字、null)

②引用类型:将该对象引用地址存储在栈中,然后对象里面的数据存放在堆中。(数组、对象、函数)

如果我们拷贝的是引用类型就要进行注意,只是单纯的复制那可不行,它们使用的还是同一个内存空间,这时我们就需要进行深拷贝,让他们互不干扰,拥有一片属于自己的小天地。

深拷贝

        let a = {
            name: '漩涡鸣人',
            age: 18
        }
        let b = JSON.parse(JSON.stringify(a)); //系统自带的JSON方法
        console.log(a);
        console.log(b);
        b.name = '宇智波佐助'
        console.log('修改新对象内容之后a的结果是', a);
        console.log('修改新对象内容之后b的结果是', b);
复制代码

深拷贝系统自带JSON.png
系统自带的JSON方法可以进行深拷贝,但是有一个缺点 函数无法使用此方法进行深拷贝

        let a = {
            name: '漩涡鸣人',
            age: 18,
            jineng: function () {
                console.log('豪火球之术');
            }
        }
        let b = Object.assign({}, a)
        console.log(a.jineng);
        console.log(b.jineng);
        b.jineng = function () {
            console.log('写轮眼');
        }
        console.log('修改新对象内容之后a的结果是', a.jineng);
        console.log('修改新对象内容之后b的结果是', b.jineng);
复制代码

深拷贝assign.png

Obje.assign当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。

        var obj = {
            a: 1,
            b: {
                c: 2,
                d: 3
            }
        }
        function deepCopy(obj) {
            // 递归 -- 自己调用自己
            var cloneObject = {}
            for (let key in obj) {
                if (obj[key] instanceof Object) {
                    // cloneObject['b'] =  {c:2,d:3}
                    cloneObject[key] = deepCopy(obj[key])
                } else {
                    cloneObject[key] = obj[key]
                }
            }
            return cloneObject;
        }
        var a = deepCopy(obj)
        //修改新对象中的数值
        a.b.c = 5
        console.log(obj)
        console.log(a);

复制代码

深拷贝递归.png
深拷贝需要使用递归方式才可以就行拷贝

深度优先遍历和广度优先遍历

其实简单来说 深度优先就是自上而下的遍历搜索 广度优先则是逐层遍历。

两者的区别

对于算法来说 无非就是时间换空间 空间换时间

  1. 深度优先不需要记住所有的节点, 所以占用空间小, 而广度优先需要先记录所有的节点占用空间大
  2. 深度优先有回溯的操作(没有路走了需要回头)所以相对而言时间会长一点

深度优先采用的是堆栈的形式, 即先进后出
广度优先则采用的是队列的形式, 即先进先出

        const data = [{
                name: '小明',
                parents: [{
                        name: '明爸爸',
                        parents: [{
                            name: '明爷爷'
                        }]
                    },
                    {
                        name: '明妈妈',
                        parents: [{
                            name: '明姥姥'
                        }]
                    },
                ],
            },
            {
                name: '小红',
                parents: [{
                        name: '红爸爸',
                        parents: [{
                            name: '红爷爷'
                        }]
                    },
                    {
                        name: '红妈妈',
                        parents: [{
                            name: '红姥姥'
                        }]
                    },
                ],
            },

        ]

        function getName(data) {
            const result = [];
            data.forEach(item => {
                const map = data => {
                    result.push(data.name);
                    data.parents && data.parents.forEach(parent => map(parent));
                }
                map(item);
            })
            return result.join(',');
        }

        // 广度遍历, 创建一个执行队列, 当队列为空的时候则结束
        function getName2(data) {
            let result = [];
            let queue = data;
            while (queue.length > 0) {
                [...queue].forEach(parent => {
                    queue.shift();
                    result.push(parent.name);
                    parent.parents && (queue.push(...parent.parents));
                });
            }
            return result.join(',');
        }

        console.log('深度---->' + getName(data))
        console.log('广度---->' + getName2(data))
复制代码

深度广度遍历.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享