浅拷贝和深拷贝的区别

189 阅读3分钟

深拷贝和浅拷贝

变量存储类型

在理解拷贝之前我们要先熟悉变量存储类型 变量的数据类型分为 基本数据类型 (值类型)和 复杂数据类型(引用数据类型) 基本数据类型的值是直接存放在栈内存的 而复杂数据类型的栈内存保存的是内存地址 值都是保存在堆内存中

浅拷贝

概念 我们可以理解为 浅拷贝就是创建一个新对象 这个对象有着被拷贝对象的对象属性值 如果是基本数据类型 拷贝的就是这个基本数据类型的值 改变新对象不会导致原来的数据发生改变 如果属性是复杂数据类型 拷贝的就是复杂数据类型的内存地址 所以如果其中一个对象改变了这个地址 就会影响到另外一个对象 就会导致原数据发生改变 这里借用GitHub中ConardLi大佬的图片来方便我们理解 image

浅拷贝使用实现:

方法一 Object.assign()

语法 Object.assign(target, … sources) 第一个参数是目标对象 第二分参数是原对象(也就是被拷贝的对象) 这是es6提供给我们的新方法 我们使用的时候需要注意兼容性的问题 还有个需要注意的是 这个方法一般用来浅拷贝对象 用来处理数组的时候 会把数组视为对象

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
}

const obj2 = Object.assign({}, obj)
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);

image

方法2 利用展开运算符进行浅拷贝

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
}

const obj2 = {...obj};
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);

image 方法3 forin循环

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
}

const obj2 = {};
for(const key in obj){
    obj2[key] = obj[key]
}
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);

image

深拷贝

概念

深拷贝指的是 将被拷贝的对象从内存中完全拷贝一份出来 在堆内存中开辟一个新的区域来存放新对象 并且修改新对象不会影响原来对象 我们在借用一次 GitHub中ConardLi大佬的图片来方便我们理解 image

方法一:递归

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
}

function deepClone(data){
    if(!data ){
        return data;
    }
    const target = Array.isArray(data)? [] :{};
    for(const key in data){
        const item = data[key];
        if(typeof item === 'function'){
          target[key] = new Function(`return ${item.toString()}`)(); 
        }else if(typeof item === 'object'){
            target[key] = deepClone(item);
        }else{
             target[key] = item
        }
    }

    return target;
}

const obj2 = deepClone(obj);
obj2.hobby.push('swim');
console.log('obj',obj);
console.log('obj2',obj2);

image

方法二 JSON.parse(JSON.stringify())

乍看 JSON.parse(JSON.stringify()) 不太明白 我们来解释一下 JSON.parse() 是一个生成新对象的方法 JSON.stringify() 是把原对象序列化为一个JSON字符串 简单的说 就是先把源对象序列化为一个JSON字符串 然后利用JSON.parse()方法转变为一个新对象 这也是我们最常用的方法 我们来使用看看结果如何:

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
};

const objString = JSON.stringify(obj);
const obj2 = JSON.parse(objString);
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);

image

有以下几种情况时,不能正确的进行深拷贝: 1.obj里面有new Date(),深拷贝后,时间会变成字符串的形式。而不是时间对象;

let a = {
     name: 'a',
     date: [new Date(1536627600000), new Date(1540047600000)],
   };

   let b = JSON.parse(JSON.stringify(a))
console.log(a,b);

image

2.obj里有RegExp、Error对象,则序列化的结果会变成空对象{};

const a = {
     name: 'a',
     date: new RegExp('\\w+'),
   };
 const b = JSON.parse(JSON.stringify(a));
 a.name = 'test'
 console.log( a, b)

image

3.obj里有function,undefined,则序列化的结果会把function或 undefined丢失

  const a = {
        name: 'a',
        date: function hehe() {
          console.log('fff')
        },
      };
      const b = JSON.parse(JSON.stringify(a));
      a.name = 'test'
      console.log(a, b)

image

4.obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null;

   const a = {
        name: 'a',
        date: NaN,
      };
      const b = JSON.parse(JSON.stringify(a));
      a.name = 'test'
      console.log(a, b)

image