深拷贝 和 浅拷贝

1. 首先需要了解一下在内存中数据的存放形式。内存中,分为了 栈区、堆区、全局/静态区、只读区。栈区主要存放局部变量值、函数命名参数,堆区存放引用类型变量。全局/静态区存放全局变量和静态变量,只读区存放常量和代码段。

2. 深拷贝和浅拷贝实际上是针对于应用类型数据来说的,因为对于基本数据类型变量来说,应该都归于深拷贝。

浅拷贝:创建一个新对象,这个新对象有原始对象属性值的一份拷贝,如果是基本数据类型,那么拷贝的是基本数据类型的值,如果是引用数据类型,那么拷贝的是它的地址,新数据和原数据会互相影响。

深拷贝:将一个对象完整的拷贝一份,存到堆区,新对象和原对象不会互相影响。

这里 Copy 别人的图片,链接:https://juejin.cn/post/6844904197595332622

当我们创建一个对象 obj,这个对象的地址被保存在栈区,而地址所指向的内容就是对象的内容,保存在堆区。当我们把这个对象直接利用赋值运算符复制一份给 obj_ 时,obj_ 存放的地址实际上和 obj 的值相同,都指向堆区中同一份空间。这就意味着这是一个浅拷贝,obj 和 obj_ 的内容改变会互相影响。

let obj = {
  name: 'James'
};
let obj_ = obj;
obj.name = 'new Name';
console.log(obj_);

 3. 如何实现深拷贝、浅拷贝?

【1】实现浅拷贝的方式:

(1)Object.assign(dest, src1, src2......) 方法,它的作用是:将 src1、src2...... 上的属性均拷贝一份,添加到 dest 对象上,如果有重复属性,默认以后一个出现的属性值作为最终值,方法返回 dest 对象。

let dest = {};
let src = {
  obj: {
    name: 'James'
  }
};
let d = Object.assign(dest, src);
console.log(d); // { obj: { name: 'James' } }
src.obj.name = 'new Name';
console.log(d); // { obj: { name: 'new Name' } }

(2)Array.prototype.slice( [ begin [, end] ] ) 方法,作用是将原数组从 begin 处开始【包含 begin 处的值】,到 end 处结束【不包含 end 处的值】的所有数组值拷贝一份,返回新的数组。

let src = [
  {
    name: 'James'
  }, 2, 3, 4
];
let dest = src.slice(); // 不写 begin、end 默认将全部值拷贝
console.log(dest); // [ { name: 'James' }, 2, 3, 4 ]
src[0].name = 'new Name';
console.log(dest); // [ { name: 'new Name' }, 2, 3, 4 ]

(3)Array.prototype.concat 方法:

let src = [
  {
    name: 'James'
  }, 5, 6, 7
];
let dest = [].concat(src);
console.log(dest); // [ { name: 'James' }, 5, 6, 7 ]
src[0].name = 'new Name';
console.log(dest); // [ { name: 'new Name' }, 5, 6, 7 ]

(4)对象展开运算符:【此处是 Copy 上面提到链接的代码】

let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }

(5)函数库 lodash 的 clone 方法

【2】实现深拷贝的方式:

(1)JSON.stringify 结合 JSON.parse 使用:但是这种方式有很大缺点,它在函数处理时无能为力,函数会被转化为null,且存在循环引用时,会出错。

let src = [
  {
    name: 'James'
  }, 8
];
let dest = JSON.parse(JSON.stringify(src));
console.log(dest); // [ { name: 'James' }, 8 ]
src[0].name = 'new Name';
console.log(dest); // [ { name: 'James' }, 8 ]

(2)函数库 lodash 的 cloneDeep 方法

(3)自己实现一个深拷贝:

function DeepClone(target) {
  if (typeof target === 'object') {
    const dest = Array.isArray(target) ? [] : {};
    for (const key in target) {
      dest[key] = DeepClone(target[key]);
    } 
    return dest;
  } else {
    return target;
  }
}

 

posted @ 2021-10-11 20:11  TwinkleG  Views(124)  Comments(0)    收藏  举报