JS 引用类型的深拷贝和浅拷贝

283 阅读2分钟

前言

  • JS数据类型共分为两种:

    • 基本类型:boolean,undefind,null,number,string;
      基本类型的数据保存在栈(stack)内存中,它们的值直接存储在变量访问的位置。
    • 引用类型:object,array,function;
      引用类型的数据保存在堆(heap)内存中,它们的值是一个指针,指向存储在堆内存中的对象(JavaScript不允许直接访问内存中的位置)。
      堆内存用于存放由new创建的对象,栈内存存放一些基本类型的变量和对象的引用变量。

对比

先定义一个包含两种数据类型的初始对象 obj :

    let obj = {
        test1: a,    // 基本类型数据 ( boolean || undefind || null || number || string )
        test2: {     // 引用类型数据 ( array || function || object )
            test3: b,// 基本类型数据
            test4: { // 引用类型数据
              ……  
            },
        },   
    } 
  • 浅拷贝:
    将要拷贝的引用类型数据遍历一层或者多层(不包括全部),转化为基本类型,然后建立新的对象存储

    • 直接赋值
          let obj_eval;
          
          如果给obj_eval赋值为 基本类型 数据,修改obj_eval不会影响obj.test1
          
          obj_eval = obj.test1;
          obj_eval = 3;
          
          console.log(obj.test1);
          
           /** 
          obj.test1 = a
          **/
          
          // 如果给obj_eval赋值为 引用类型 数据,修改obj_eval也会同时修改obj.test2(因为引用类型数据保存的是一个指向栈内存的指针,所以修改任何一个拷贝的数据都会修改保存在内存中的数据)
          
          obj_eval = obj.test2;
          obj_eval.test3 = b1;
          obj_eval.test4 = b2;
          
          console.log(obj.test2);
          
          /** 
          obj.test2 = {
              test3: b1,
              test4: b2,
          }
          **/
      
    • 拷贝一层或者多层(不包括全部)
        直接赋值引用类型的数据会引起原数据的改变,所以需要新建一个对象,然后遍历获得原始数据的属性名称和值,再绑定到新建的对象中再返回这个对象。
        
        
        //新建一个拷贝对象
        let obj_shallow_copy;
        
        //定义方法遍历原数据的属性名称和值 并新建一个临时对象备份存储 然后返回
        function shallow_copy(obj) {
            let obj_copy = {};
            for (let prop in obj) {
                if (obj.hasOwnProperty(prop)) {
                    obj_copy[prop] = obj[prop];
                }
            }
            return obj_copy;
        };
        
        //赋值给新建的对象
        obj_shallow_copy = shallow_copy(obj); //可多次调用该函数
        obj_shallow_copy.test1 = a1;
        obj_shallow_copy.test2 = b1;
        
        console.log(obj);
        console.log(obj_shallow_copy)
        /** 
        obj = {
            test1: a,
            test2: b1,
        };
        obj_shallow_copy = {
            test1: a1,
            test2: b1,
        }
        **/
    
  • 深拷贝:
    将要拷贝的引用类型数据全部遍历递归,转化为基本类型,然后建立新的对象存储

    let obj_deep_copy = deep_copy(obj);
    
    //定义深拷贝方法
    function deep_copy(obj) {
        //先判断obj是否为引用类型
        if(obj === null || typeof obj !== 'object'){
            return obj;
        }
        //再判断obj是引用类型中的哪一类
        let obj_copy;
        if(obj instanceof Array){
            obj_copy = [];
            for(let i=0;i<obj.length;i++){
                obj_copy.push(deep_copy(obj[i]));       //递归
            };
        }else if(obj instanceof Object){
            obj_copy = {};
            for(let item in obj){
                obj_copy[item] = deep_copy(obj[item]);  //递归
            }
        };
        return obj_copy;
         
    }
    obj_deep_copy.test1 = a1;
    obj_deep_copy.test2 = b1;
    
    console.log(obj);
    console.log(obj_shallow_copy);
    
    /**
    obj = {
        test1: a,
        test2: b,
    };
    obj_shallow_copy = {
        test1: a1,
        test2: b1,
    };
    **/

更多深拷贝方法参考:深入剖析 JavaScript 的深复制