美文网首页
对象的浅拷贝和深拷贝

对象的浅拷贝和深拷贝

作者: keknei | 来源:发表于2019-06-05 16:12 被阅读0次

    我们先定义一个对象,包含数字、数组、对象、函数、undefined等类型

    let obj={
      a:1,
      arr:[1,2,3],
      o:{
        x:0
      },
      fn:function (){
        console.log(1);
      },
      b:undefined
    }
    

    浅拷贝就只是复制对象的引用(堆和栈的关系,简单类型Undefined,Null,Boolean,Number和String是存入堆,直接引用,object array 则是存入桟中,只用一个指针来引用值)

    我们先来个浅拷贝,我们改变a的属性,再改变数组里第一项的值,再看看最后两个对象是什么结果

    let obj2=obj;
    obj2.a=2;
    obj2.arr.splice(0,1,10);
    console.log(obj);
    console.log(obj2);
    

    我们改变的是obj2拷贝对象的值,但是被拷贝对象obj也改变了,这个就是浅拷贝

    浅拷贝的结果
    深拷贝我们介绍三种方法
    1. 第一种方法 野路子 JSON.parse 是将一个 JSON 字符串转成一个 JavaScript 值或对象
    let obj3=JSON.parse(JSON.stringify(obj));
    obj3.a=3;
    obj3.arr.splice(0,1,11);
    console.log(obj3);
    

    我们可以看到这种方法中undefined、function、symbol 会在转换过程中被忽略。。。所以如果拷贝对象中有这三种类型的话,是不能用这种方法的


    深拷贝第一种方法的结果
    1. 第二种方法 递归的方法
    function deepClone(obj){
      let tempObj=obj.constructor==Array?[]:{};//判断copy对象的父级是数组还是对象
      for(let name in obj){
        if(obj.hasOwnProperty(name)){//是自身属性
          if(obj[name] && typeof obj[name]=="object"){//属性值如果是对象,就继续循环递归下去
            tempObj[name]=deepClone(obj[name]);
          }else{//如果不是,就直接赋值
            tempObj[name]=obj[name];
          }
        }
      }
      return tempObj;
    }
    let obj4=deepClone(obj);
    obj4.a=4;
    obj4.arr.splice(0,1,4);
    console.log(obj4);
    

    这种方法是比较完美的,如果拷贝对象中有函数、undefined类型,推荐用这种方法,是可以拷贝的


    第二种拷贝对象的方法
    1. 第三种方法 MessageChannel
    function deepCopy(obj){
      return new Promise(resolve=>{
        const {port1,port2}=new MessageChannel();
        console.log(port1);
        port2.onmessage=event=>{
          console.log(event)
          resolve(event.data);
        };
        port1.postMessage(obj);
      });
    }
    deepCopy(obj).then(res=>{
      let obj5=res;
      obj5.a=5;
      obj5.arr.splice(0,1,5);
      console.log(obj5);
    });
    

    我们运行之后会发现拷贝对象中有函数的对象时,还是会报错,所有说如果拷贝对象中有函数是不能用MessageChannel这种方法的

    拷贝对象中有函数就会报错
    当我们把拷贝对象的函数去掉之后,再看结果:
    第三种拷贝方法
    可以看到当拷贝对象中没有函数时,是可以拷贝;
    因为MessageChannel是异步的,所有我们用promise来运行

    ...拓展运算符来实现拷贝

    let obj6={...obj};
    obj6.a=6;
    obj6.arr.splice(0,1,6);
    console.log(obj6);
    

    这种方式只能实现首层浅拷贝,就是对象第一层的深拷贝。后面的深层次的只是拷贝的引用值


    拓展运算符来实现拷贝

    Object.assign()来实现拷贝

    let obj7=Object.assign({},obj);
    obj7.a=7;
    obj7.arr.splice(0,1,7);
    console.log(obj7);
    

    我们可以看到这种方式拷贝和拓展运算符来实现拷贝方法一样的效果,都是对第一层实现深拷贝,对深层次的是浅拷贝


    Object.assign()来实现拷贝

    数组的首层浅拷贝三种方法

    1. concat方法
    let arr1=[
      1,
      [1,32,4],
      {
        a:1
      }
    ];
    let arr2=arr1.concat();
    arr2[0]=2;
    arr2[2].a=2;
    console.log(arr1);
    console.log(arr2)
    

    通过下面的运行结果可以看到,首层第一项只改变了本身,被拷贝的数组没有改变,而最后一项的对象全部改变了,说明concat方法只能深拷贝首层,多层次的无法深拷贝,还是引用


    concat方法
    1. slice方法
    let arr1=[
      1,
      [1,32,4],
      {
        a:1
      }
    ];
    let arr2=arr1.slice();
    arr2[0]=2;
    arr2[2].a=2;
    console.log(arr1);
    console.log(arr2)
    

    通过运行结果,我们发现sliceconcat一样运行结果,都是首层浅拷贝

    slice方法
    1. ...拓展运算符
    let arr1=[
      1,
      [1,32,4],
      {
        a:1
      }
    ];
    let arr2=[...arr1];
    arr2[0]=2;
    arr2[2].a=2;
    console.log(arr1);
    console.log(arr2)
    

    通过运行结果,我们发现...拓展运算符concat一样运行结果,也是首层浅拷贝

    ...拓展运算符

    相关文章

      网友评论

          本文标题:对象的浅拷贝和深拷贝

          本文链接:https://www.haomeiwen.com/subject/mzdrxctx.html