数据类型存储

JavaScript中存在两大数据类型:

  • 基本类型
  • 引用类型

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

浅拷贝

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址
即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址

  1. function shallowClone(obj) {
  2. const newObj = {};
  3. for(let prop in obj) {
  4. if(obj.hasOwnProperty(prop)){
  5. newObj[prop] = obj[prop];
  6. }
  7. }
  8. return newObj;
  9. }

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

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

    深拷贝

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

  • _.cloneDeep()

  • jQuery.extend()
  • JSON.stringify()—这种方式存在弊端,会忽略undefined、symbol和函数
  • 手写循环递归

    1. function deepClone(obj, hash = new WeakMap()) {
    2. if (obj === undefined) return undefined // undefined 的情况
    3. if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
    4. if (obj instanceof Date) return new Date(obj);//日期对象的情况
    5. if (obj instanceof RegExp) return new RegExp(obj);//正则表达式的情况
    6. // 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
    7. if (typeof obj !== "object") return obj;
    8. // 是对象的话就要进行深拷贝
    9. if (hash.get(obj)) return hash.get(obj);
    10. let cloneObj = new obj.constructor();
    11. // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
    12. hash.set(obj, cloneObj);
    13. for (let key in obj) {
    14. if (obj.hasOwnProperty(key)) {
    15. // 实现一个递归拷贝
    16. cloneObj[key] = deepClone(obj[key], hash);
    17. }
    18. }
    19. return cloneObj;
    20. }

    深拷贝循环引用问题

    概念:对象 A 中包含指向对象 B 的指针,对象 B 中包含指向对象 A 的指针,会引发内存泄漏现象。
    解决:利用 ES6 中 WeakMap 或者 Map 数据结构来存储每一次复杂类型的值,如果下次又出现了该值,就不再进行拷贝,直接截止。

    区别

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

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址

  • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址