浅拷贝与深拷贝(面试题)

578 阅读3分钟

一.浅拷贝

1.拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存,然而当内存销毁的时候,指向这个内存空间的所有指针需要重新定义,不然会造成野指针错误。

浅拷贝只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存

2.浅拷贝出来的对象就是外新内旧的对象,对象本身(id)和原始对象完全不同,但是子对象和原始对象的子对象是一样的。

3.浅拷贝是将对象的引用复制给另一个对象。因此,如果我们在子对象中进行更改,则会影响原对象;但对对象本身修改并不会影响原始对象。使用 copy()函数进行浅拷贝。

4.经常会有人说赋值也是浅拷贝,其实这种说法是不严谨的,因为赋值本质上只是同一个引用,并不是真正的浅拷贝。 浅拷贝只复制一层对象的属性,并不会进行递归复制,而在浅拷贝之后,源对象与目标对象的引用已经不是同一块内存空间。 JavaScript 存储对象都是存储地址的,浅拷贝导致更深一层的对象可能会指向同一个内存地址。

5.在浅拷贝时,拷贝出来的新对象的地址和原对象是不一样的,但是新对象里面的可变元素(如列表)的地址和原对象里的可变元素的地址是相同的,也就是说浅拷贝它拷贝的是浅层次的数据结构(不可变元素),对象里的可变元素作为深层次的数据结构并没有被拷贝到新地址里面去。

二.深拷贝

1.概念:

创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”,新对象跟原对象不共享内存,修改新对象不会改到原对象

2.深拷贝作用在引用类型上!例如:Object,Array
深拷贝不会拷贝引用类型的引用,而是将引用类型的值全部拷贝一份,形成一个新的引用类型,这样就不会发生引用错乱的问题,使得我们可以多次使用同样的数据,而不用担心数据之间会起冲突。

3.通过深拷贝来的对象完全没有变化,说明深拷贝来的对象本身以及包含的子对象都完完全全是新的,和原对象没有一点关系。

4.深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大

三.浅拷贝与深拷贝:json实现

代码如下:


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        /* 
        1.递归函数:  一个函数 在内部 调用自己
            * 递归作用和循环类似的,也需要有结束条件

        2.递归应用:
            浅拷贝与深拷贝 : 
                方式一(推荐) : JSON方式实现
                    * let newObj = JSON.parse( JSON.stringify( obj ) )
                方式二(递归) : 了解
            遍历dom树
        */

        /* let obj = {
            name:'张三',
            age:20,
            sex:'男',
            hobby:['吃饭','睡觉','学习']
        } */

        //浅拷贝: 拷贝地址
        // let newObj = obj
        // 修改拷贝后的数据,原数据也会修改
        // newObj.name = '李四'
        // console.log( obj,newObj)

        //深拷贝 : 拷贝数据
        //(1)先把js对象 -> JSON字符串   (JSON会自动帮你深拷贝)
        // let jsonStr = JSON.stringify( obj )
        //(2)再把 JSON字符串 -> js对象
        // let newObj = JSON.parse( jsonStr )

        /*  let newObj = JSON.parse( JSON.stringify( obj ) )
 
         newObj.name = '李四'
         console.log(obj,newObj) */

      
        1.浅拷贝与深拷贝
        浅拷贝: 拷贝地址, 修改拷贝后的数据对原数据有影响
        深拷贝: 拷贝数据, 修改拷贝后的数据对原数据没有影响
        2.深拷贝实现方式
        2.1使用json:
        2.2使用递归: */
        let obj = {
            name: '张三',
            age: 20,
            hobby: ['学习', '打游戏', '干饭']
        }
        // 1.浅拷贝: 拷贝地址
         let newObj = obj
        newObj.name = '李四'
        newObj.hobby[0] = '游戏'
        console.log(obj, newObj) //修改newObj,obj也会修改
        // 2.使用json进行深拷贝
        // (1)把obj转成json格式字符串:底层会自动深拷贝
        // let jsonStr = JSON.stringify(obj)
        // console.log(jsonStr)
        // (2)把json转成对象
        // let newObj = JSON.parse(jsonStr)

         let newObjOne = JSON.parse(JSON.stringify(obj))
        newObjOne.name = '前期'
        newObjOne.hobby[0] = '看电影'
        console.log(obj,newObjOne) 
    </script>
</body>

</html>

效果如下:

image.png

四.深拷贝:递归实现

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        /* 
        1.递归函数:  一个函数 在内部 调用自己
            * 递归作用和循环类似的,也需要有结束条件

        2.递归应用:
            浅拷贝与深拷贝 : 
                方式一(推荐) : JSON方式实现
                    * let newObj = JSON.parse( JSON.stringify( obj ) )
                方式二(递归) : 了解
            遍历dom树
        */

        /* let obj = {
            name:'张三',
            age:20,
            sex:'男',
            hobby:['吃饭','睡觉','学习'],
            student:{
                name:"李四",
                score:90
            }
        }

        //使用递归函数
        function copy(obj,newObj){
            for(let key in obj){
                if( obj[key] instanceof Array ){
                    //声明一个空数组,然后继续拷贝数组里面的数据
                    newObj[key] = []
                    //递归调用继续拷贝 数组
                    copy(obj[key],newObj[key])
                }else if(  obj[key] instanceof Object ){
                     //声明一个空对象
                     newObj[key] = {}
                    //递归调用继续拷贝 对象
                    copy(obj[key],newObj[key])
                }else{
                    newObj[key] = obj[key]
                }
            }
        }
        //创建一个空对象,然后深拷贝
        let newObj = {}
        copy(obj,newObj)

        newObj.name = '李四'
        newObj.hobby[0] = '摸鱼'
        newObj.student.name = '王五'
        console.log( obj,newObj) */

        let obj = {
            name: '张三',
            age: 33,
            hobby: ['学习', '上课', '干饭'],
            student: {
                name: '尼古拉斯凯奇',
                age: 66
            }
        }
        // 深拷贝函数
        function copy(obj, newObj) {
            for (let key in obj) {
                if (obj[key] instanceof Array) {
                    newObj[key] = []
                    // 递归调用,继续深拷贝数组
                    copy(obj[key],newObj[key])
                }else if(obj[key] instanceof Object){
                    newObj[key] = {}
                    // 递归调用,继续深拷贝对象
                    copy(obj[key],newObj[key])
                }else{
                    newObj[key] = obj[key] 
                }
            }
        }
        // 开始拷贝
        let newObj = {}
        copy(obj,newObj)
        newObj.name = '嗯嗯嗯'
        newObj.hobby[0] = '电影'
        newObj.student.name = 'uuuuuuu'
        console.log(obj,newObj)
    </script>
</body>

</html>

效果如下:

image.png

五.总结

  • 浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个 地址,就会影响到另一个对象。

  • 深拷贝:会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即 发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。