浅拷贝与深拷贝

146 阅读3分钟

深拷贝浅拷贝的区别?如何实现一个深拷贝?

一、数据类型存储

JavaScript中存在两大数据类型:

  • 基本类型 Number String null Undefined Boolean
  • 引用类型 array object function

基本类型数据保存在在栈内存中

引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中

二、浅拷贝

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝

如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址

即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址

 /* 
        1.浅拷贝 与 深拷贝
            浅拷贝: 拷贝地址, 修改拷贝后的数据对原数据有影响
            深拷贝: 拷贝数据, 修改拷贝后的数据对原数据没有影响
        
        2.深拷贝实现方式
            2.1 使用JSON : let newObj = JSON.parse( JSON.stringify( obj ) )
            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 newObj = JSON.parse( JSON.stringify( obj ) )
        
        newObj.name = '李四'
        newObj.hobby[0] = '游戏'
        console.log(obj,newObj)
        
}

三、深拷贝

深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

常见的深拷贝方式有:

  • JSON.stringify()
  • 手写循环递归
                                                   

 1.递归函数: 一个函数 在内部 调用自己                              
 * 递归作用和循环类似的,也需要有结束条件                              
                                                    
 2.递归应用:                                            
     浅拷贝与深拷贝 :                                          
    方式一(推荐) : JSON方式实现                                 
    * let newObj = JSON.parse( JSON.stringify( obj ) ) 
    方式二(递归) : 了解                                       
    遍历dom树                                             
    */                                                 
                                                       
    let obj = {                                        
    name: '张三',                                        
    age: 20,                                           
    sex: '男',                                          
   hobby: ['吃饭', '睡觉', '学习'],                         
   student: {                                         
    name: "班长",                                        
   score: 90                                          
     }                                                  
     }                                                                                                    
     //使用递归函数                                           
     function kaobei(obj, newObj) {                     
     for (let key in obj) {                             
     if (obj[key] instanceof Array) {                   
     //声明一个空数组,然后继续拷贝数组里面的数据                            
     newObj[key] = []                                   
     //递归调用继续拷贝 数组                                      
     kaobei(obj[key], newObj[key])                      
       } else if (obj[key] instanceof Object) {           
      //声明一个空对象                                          
    newObj[key] = {}                                   
     //递归调用继续拷贝 对象                                      
     kaobei(obj[key], newObj[key])                      
     } else {                                          
     newObj[key] = obj[key]                             
     }                                                  
     }                                                  
     }                                                  
     //创建一个空对象,然后深拷贝                                    
     let newObj = {}                                    
  kaobei(obj, newObj)                                
                                                        
    newObj.name = '李四'                                 
     newObj.hobby[0] = '摸鱼'                             
        newObj.student.name = 'ikun'   
    console.log(obj, newObj)

JSON.stringify()

const obj2=JSON.parse(JSON.stringify(obj1));

但是这种方式存在弊端,会忽略undefined和函数

四、区别

Snipaste_2022-05-22_16-03-01.png

从上图发现,浅拷贝和深拷贝都创建出一个新的对象,但在复制对象属性的时候,行为就不一样

浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,修改对象属性会影响原对象

小结

前提为拷贝类型为引用类型的情况下:

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
  • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址

总结:

  • 深拷贝递归地复制新对象中的所有值或属性,而拷贝只复制引用。
  • 在深拷贝中,新对象中的更改不会影响原始对象,而在浅拷贝中,新对象中的更改,原始对象中也会跟着改。
  • 在深拷贝中,原始对象不与新对象共享相同的属性,而在浅拷贝中,它们具有相同的属性。