Fork me on GitHub

JS 深拷贝 VS 浅拷贝

复习到深浅拷贝问题,简单Mark一下实现方法。

## 写在前面

首先我们得清楚基本概念。拷贝(Copy)即复制。

浅拷贝:创建一个新对象,保存原始对象属性值精准拷贝。如果属性是基本类型,拷贝的是基本类型的值,如果属性是引用类型,拷贝的是内存地址,并不会占用新的内存,这种情况下如果其中一个对象改变了这个地址,会影响到另一个对象。浅拷贝只复制指向某个对象的指针,而不复制对象本身。**新旧对象共享同一块内存**。

深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,增加了内存,且修改新对象不会影响原对象。**新对象与原对象不共享内存**。

## 赋值和深/浅拷贝的区别(针对引用类型)

赋值:把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。

浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,前后的两个对象互不影响。

## 浅拷贝的实现方案

### 0x01 Object.assign()
把任意多个源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

let obj2 = Object.assign({}, obj1)

 

### 0x02 函数库lodash的_.clone方法

var _ = require('lodash');

var obj2 = _.clone(obj1);

 

### 0x03 展开运算符
同object.assign()功能相同

let obj2 = {...obj1}

 

### 0x04 Array.prototype.concat()

let arr2 = arr1.concat()

 

### 0x05 Array.prototype.slice()

let arr2 = arr1.slice()

 

## 深拷贝的实现方案

### 0x01 JSON.parse()和JSON.stringify()

let arr2 = JSON.parse(JSON.stringify(arr1));

 

缺点是不能处理函数和正则

### 0x02 函数库lodash的_.cloneDeep方法

var _ = require('lodash');

var obj2 = _.cloneDeep(obj1);

 

### 0x03 jQuery.extend()方法

$.extend(deepCopy,target,obj1,[objN]) // 第一个参数为true就是深拷贝

 

### 0x04 手写递归实现
解决循环引用的问题

function deepClone(obj, hash=new WeakMap()) {
  if(obj == null) return obj; // 不操作
  if(obj instanceof Date) return new Date(obj);
  if(obj instanceof RegExp) return new RegExp(obj);
  // 普通值/函数不需要深拷贝
  if(typeof obj !== "object") return obj;
  // 是对象的话要进行深拷贝
  if(hash.get(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor();
  // 找到的是所属类原型上的constructor,而原型上的constructor指向的是当前类本身
  hash.set(obj. cloneObj);
  for(let key in obj) {
    if(obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash)
    }
  }
  return cloneObj;
}

 

posted @ 2020-09-13 19:24  Lynn_z  阅读(146)  评论(0编辑  收藏  举报