浅拷贝和深拷贝

吐槽君 分类:javascript

浅拷贝和深拷贝的考题重心我认为是在 对 基本类型 和引用类型 的理解深度

抛出问题

  • 当我们
var a = 1;
var b = a;// 这里都可知 a和b都为1
//当我们 再执行
var b = 2;
//最后 b 的值为 2   a 的值 为1 
 
  • 如果
var a1 = new Array(1,2,3);
var a2 = a1;
//当我们
var a2[0] = 4;
//  这时候 a2[0] = 4;  而a1[0] = 4;
 

也就是说,我们并没有拷贝到a1这个对象内部的值,仅仅是共用而已。这里的主要区别是 a 和 a1 ,一个是基本类型,另一个是引用类型。此处推荐链接,此大佬写的非常好理解
:blog.csdn.net/jiang770103…

那么问题就出来了,一开始 a2 为空,我们该怎么拿到 a1 的东西,复制给 a2 ,并且 a2 想改变某些东西,不会影响到 a1 ,,相当于我们去修改一个文档,并且修改后另存为 一个文档。我们把这个方法叫做 拷贝

拷贝

浅拷贝

  • 就如这样
var a1 = {
    'id':'01',
    'name':'aaa'
}
var a2 = {}; //a2 为空对象
for (let key in a1){
    a2[key] = a1[key];  //把 a1 的东西 拷贝 给a2
}
a2.id = '02';  //改变 a2  中id的值 改为 02
console.log(a1);
console.log(a2);
 
  • 我们 a2 拿到了 a1 并且改变了id为2 ,并且没有影响到 a1

image.png

  • 那么问题又来了,假如 a1 里面有 一个 sex 属性,sex属性也为一个对象,就是说 对象a1 的属性是对象,也能如愿实现吗?
var a1 = {
    'id':'01',
    'name':'aaa',
    'sex': new Array('man','woman') //这里sex也是一个对象
}
var a2 = {};
for (let key in a1){
    a2[key] = a1[key];
}
a2.sex[0]= '1111'; // 改变sex的第一个属性值
console.log(a1);
console.log(a2);
 

image.png

  • 答案是不能

深拷贝

在深拷贝面前来形容浅拷贝,那只能说浅拷贝 拷贝的深度不够,拷贝的层次还在第一层,也就是 a1 这个对象层上

  • 我是这么形容 引用类型的

引用类型,在拷贝的时候,我们相当于另外开辟了内存,拿上面例子来说,解释一下为什么不能!当a1 中的 id name sex 被存起来后,生成了一个地址 ,地址给了 a1 ,我们拷贝过后 实际上是复制了一模一样的东西,只不过是 对象名改了,地址不一样, a2 也能随意拿到它地址下的 id name sex ,但是 sex 也是个对象 ,在 a1 中同样 sex有 man woman 的地址 ,而 我们拷贝的时候没有拷贝到 sex ,也就是说我们拷贝到a1 下面就停止了,导致 a2 下的 sex 下的地址仍然是a1下 的sex 的 ,所以我们要把sex 对象也要拷贝到 a2 里面去

做法

  • 先考虑对象里面的属性有没有是对象,如果是 ->
  • a2[key][i] = a1[key][i],深拷贝。

简陋先实现一下

var a1 = {
    'id':'01',
    'name':'aaa',
    'sex': new Array('man','woman')
}
var a2 = {};
for (let key in a1){
    if( typeof a1[key] == 'object'){
        a2[key] = [];  //a2.[key] 是一个对象,这里初始化为个空数组
        for(let i in a1[key]){
            a2[key][i] = a1[key][i]  // 第二层的拷贝
        }
    }else{
        a2[key] = a1[key];
    }
}
a2.sex[0] = '1111';
console.log(a1);
console.log(a2);
 

image.png

  • 那如果布置两层的嵌套呢? 这时候我们就需要用到递归了

递归一下

var a1 = {
    'id':'01',
    'name':'aaa',
    'sex': new Array('man','woman'),
    'class':{
        'classname':{
            'classid':'111',
            'classnum':'222'
        }
    }
}

function  kaobei(a1) {
    var a2 = {};
    for (let key in a1){
        if( typeof a1[key] == 'object'){
            // a2[key] = [];
            
            a2[key] = kaobei(a1[key]) //调用递归
            
        }else{
            a2[key] = a1[key];
        }
    }
    return a2
}

// a2.sex[0] = '1111';
let a3 = kaobei(a1);
a3.class.classname.classnum = '333'
console.log(a1);
console.log(a3);
 

image.png

有BUG

方法已经大致实现了,小伙伴们是否发现了小bug ?

  1. 当遇到两个互相引用的对象,会出现死循环的情况(循环引用)
  2. 对于data、RegExp这些类型时,要特殊处理
  3. 针对遍历对象不可枚举的属性和Symbol 类型怎么处理?

这bug真不小,里面用到的Weakmap.set和get 方法,解决循环引用,getOwnPropertyDescriptors 用来返回 指定对象所有自身属性(非继承属性) 的描述对象 Object.create 进行继承原型链,前面两个说实话我是第一次见,说到底还是书读少了!

const obj = {
    a: 1,
    b: 2,
    c: {
      aa: 1,
      bb: "2",
      cc: true,
      dd: [1, 1, 3, 4, 5],
      ee: { c4: 3 },
    },
    d: [1, 2, 3, 4, 5],
    e: [{ a1: 1 }, { b2: 2 }, { c3: 3 }],
  };
  obj.f = obj;
  
  const clone = (c) => {
    const isObject = (o) => {
      if (o === null || o === undefined) return o;
      else return Object.prototype.toString.call(o).slice(8, -1);
    };
  
    // WeakMap用来保存已经复制过的object, WeakMap<Object, boolean>
    let map = new WeakMap();
  
    const dp = (object) => {
      let result;
      // 判断当前复制操作时的对象的类型
      if (isObject(object) == "Object") result = {};
      else if (isObject(object) == "Array") result = [];
      // 基础类型则直接返回
      else return object;
  
      for (const key in object) {
        const keyType = isObject(object[key]);
        const copy = object[key];
        if(map.get(copy)){
          // 如果这个对象已经复制过,则不递归操作
          return copy
        } else if (keyType == "Object") {
          // 当前是对象时候则先缓存
          map.set(copy, true)
          result[key] = dp(copy);
        } else if (keyType == "Array") {
          result[key] = dp(copy);
        } else {
          result[key] = copy;
        }
      }
      return result;
    };
    const res = dp(c)
    return res
  };
  let obj1 = obj;
  console.log(clone(obj1));
  
 

image.png

这里推荐个链接学习一下WeakMap :blog.csdn.net/Ed7zgeE9X/a…

总结

本人也是第一次学习到这个深浅拷贝,有很多地方都没有学精,但却给我深深上了一课,自己也感觉能够慢慢提升自己的思维能力,在写掘金文章,能够让自己学的更透彻,什么了解过,什么已经掌握了。给自己打气,加油吧!在文章中扎实,稳步,快速地成长!

回复

我来回复
  • 暂无回复内容