likes
comments
collection
share

深拷贝浅拷贝的区别?如何实现一个深拷贝?

作者站长头像
站长
· 阅读数 26

数据类型存储

JavaScript中存在两大数据类型:

  • 基本类型
  • 引用类型

基本类型数据保存在在栈内存中

引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中

浅拷贝

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝

如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址

即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址

function shallowClone(obj) {
  const newObj = {};
  for(let prop in obj) {
    if(obj.hasOwnProperty(prop)){
      newObj[prop] = obj[prop];
    }
  }
  return newObj;
}

在JavaScript中,存在浅拷贝的现象有:

  • Object.assign
  • Array.prototype.slice(), Array.prototype.concat()
  • 使用拓展运算符实现的复制

深拷贝

深拷贝开辟一个新的栈,两个对象属性完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

常见的深拷贝方式有:

  • _.cloneDeep()
  • jQuery.extend()
  • JSON.stringify()--这种方式存在弊端,会忽略undefined、symbol和函数
  • 手写循环递归
function deepClone(obj, hash = new WeakMap()) {
  if (obj === undefined) return undefined // undefined 的情况
  if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
  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;
}

深拷贝循环引用问题

概念:对象 A 中包含指向对象 B 的指针,对象 B 中包含指向对象 A 的指针,会引发内存泄漏现象。

解决:利用 ES6 中 WeakMap 或者 Map 数据结构来存储每一次复杂类型的值,如果下次又出现了该值,就不再进行拷贝,直接截止。

区别

前提为拷贝类型为引用类型的情况下:

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
  • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
转载自:https://juejin.cn/post/7087457326805975076
评论
请登录