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
 
posted @ 2020-09-08 21:04  JeanJY  阅读(160)  评论(0编辑  收藏  举报