zoukankan      html  css  js  c++  java
  • JavaScript之浅拷贝与深拷贝

          何为浅拷贝、深拷贝?我们在项目中可能会经常对数组、对象进行备份 但是你发现我们操作原数组或对象时,会把备份的数据也改变了。

          这种情况下,你或与会感到疑惑,此时就需要你对拷贝有一点深入的了解

    一、浅拷贝

       1.  定义 

                    只是把数组、对象的第一级拷贝,赋值给新的数组。一般我们实现的数组拷贝方法都是浅拷贝

          

             2. 方法

          (1) slice(针对数组)  基于当前数组中一个或多个创建新数组  arr.slice(0) ->从arr数组的索引为0的位置到最后一个,截取到新数组

       var arr=[5,'hello',true,undefined,[4,2,1],{n:2}];
        var arr2=arr.slice(0)
        console.log(arr2)// [5,'hello',true,undefined,[4,2,1],{n:2}]
        console.log(arr2[5]===arr[5]);// true 
         
        arr[5].n="change"  // 改变arr中{n:2}的值
        console.log(arr2[5].n) //arr2中的{n:2}中n的值也变为change

       注: 由上图的arr[5].n="change"改变了,arr2[5].n的值也改变,可以看出浅拷贝只拷贝了第一层,对于第一层中的对象,数组,也只是拷贝了他们的引用(也就是指向同一个地址),而非重新开辟一个新的内存空间。

        所以无论是对arr的{n:2}或[4,2,1]进行修改,arr2 都会反映出修改

    (2)concat(针对数组)  基于当前数组中所有项创建新数组

        var arr=[5,'hello',true,undefined,[4,2,1],{n:2}];
        var arr2=arr.concat()
        console.log(arr2)// [5,'hello',true,undefined,[4,2,1],{n:2}]
        console.log(arr2[5]===arr[5]);// true 
        arr[5].n="change"  // 改变arr中{n:2}的值
        console.log(arr2[5].n) //arr2中的{n:2}中n的值也变为change
     

        (3)扩展运算符(对象数组)

       var arr=[5,'hello',true,undefined,[4,2,1],{n:2}];
       var arr2=[...arr]
       console.log(arr2)// [5,'hello',true,undefined,[4,2,1],{n:2}]
       console.log(arr2[5]===arr[5]);// true 
       arr[5].n="change"  // 改变arr中{n:2}的值
       console.log(arr2[5].n) //arr2中的{n:2}中n的值也变为change
    
      // 如果拷贝是对象
      var obj={a:1,name:"joy"};
      var obj2={...obj}
      console.log(obj2)// {a: 1, name: "joy"}

       (4)object.assign()(对象数组都可)- >能够拷贝源对象自身的并且可枚举的属性到目标对象

     // 如果拷贝是数组
        var arr=[5,'hello',true,undefined,[4,2,1],{n:2}];
        var arr2=Object.assign([],arr)
        console.log(arr2)// [5,'hello',true,undefined,[4,2,1],{n:2}]
    
    
      // 如果拷贝是对象
         var obj={a:1,name:"joy"};
         var obj2=Object.assign({},obj)
         console.log(obj2)/a/ {a: 1, name: "joy"}

     (5)自定义方法(对象数组都可)

        let obj={
          a:10,
          b:[0,20,5],
          c:{x:10},
          d:/^d+$/
        }
        let obj2={}
        // for-in 会遍历出对象和其原型上可枚举的属性
        for(let key in obj){
          // 判断该属性是否为对象本身的属性
          if(!obj.hasOwnProperty(key)) break;
          obj2[key]=obj[key]
        }
    
        console.log(obj2); //{a: 10, b: Array(3), c: {x:10}, d: /^d+$/}
        console.log(obj2===obj) //false
        console.log(obj2.c===obj.c) //true
    
        obj2.c.x=200;
        console.log(obj.c.x) //200

    二、深拷贝

       1.定义

         不仅把第一级拷贝一份新的对象,如果原始对象中存在多级,那么是把每一级都拷贝一份复制给新对象的每一个级别

       

         2.方法

         (1)利用JSON数据格式

        应用:对数字、字符串、布尔值、普通对象、数组等没有影响

        缺点:JSON.stringify(obj)并非symbol对所有值都有处理,正则 => {},函数 / undefined / Symbol => null ,日期格式变成字符串后,基于parse回不到对象格式了

      let obj={
          a:10,
          b:[0,20,5],
          c:{x:10},
          d:/^d+$/,
          e:new Date(),
          f:function(){return "hello"},
          h:undefined
        }
    
       console.log(new Date()) //日期格式Tue Sep 08 2020 20:18:59 GMT+0800
    
       let obj2=JSON.parse(JSON.stringify(obj));
    
       console.log(obj2) 
       //{a: 10, b:[0,20,5], c:{x:10}, d: {}, e: "2020-09-08T12:21:34.834Z"}
       // 由结果可看出,JSON.stringify(obj)会将日期格式变成字符串后基于parse回不到对象格式了
       //              正则变为{},函数/undefind都变成了null
    注:JSON.stringify(obj)->先把原始对象变为一个字符串,去除堆与堆嵌套关系
       JSON.parse(JSON.stringify(obj))->把字符串转化为新对象,这样浏览器会重新开辟内存来存储信息

    (2)自己封装
       let obj={
          a:10,
          b:[0,20,5],
          c:{x:10},
          d:/^d+$/,
          e:new Date(),
          f:function(){},
          h:undefined
        }
    
    function deepClone(obj){
      // 过滤出特殊情况
      if(obj===null){
        return null
      }
      if(typeof obj !=='object') return obj;
      if(obj instanceof RegExp){
        return new RegExp(obj)  
      }
      if(obj instanceof Date){
        return new Date(obj)
      }
      // obj.constructor 不直接创建空对象的目的:拷贝的结果和之前保持相同的属类
      let newObj=new obj.constructor();
      for(let key in obj){
        if(!obj.hasOwnProperty(key)) break;
        newObj[key]=deepClone(obj[key])
      }
      return newObj;
    
    }
    let obj2=deepClone(obj);
    console.log(obj2)
    
    console.log(obj===obj2) //false
    console.log(obj.c===obj2.c) //fasle
    
    // 数组
    // let obj=[10,[0,20,5],
    //         {x:10},/^d+$/,
    //         new Date(),undefined,function(){}
    //       ]   
    // let obj2=deepClone(obj);
    // console.log(obj2)
    // console.log(obj===obj2) //false
    // console.log(obj.c===obj2.c) //fasle
     
  • 相关阅读:
    Android获取View对应的Bitmap
    Android按需添加Google Play服务
    Android自定义View的构造函数
    两个Fragment之间如何传递数据
    SmartImageView
    onSingleTapUp()和onSingleTapConfirmed()的区别
    Android Fragment add/replace以及backstack
    InputStream与InputStreamReader的区别
    Android手动签名
    使用后台服务数据更新UI
  • 原文地址:https://www.cnblogs.com/Jeanchjy/p/13632575.html
Copyright © 2011-2022 走看看