浅拷贝和深拷贝

深拷贝和浅拷贝

简介

  • 浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
  • 深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
  • 总而言之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

浅拷贝的实现方式

  • Object.assign()

    1. Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
    2. 用法:Object.assign({},obj)
  • 展开运算符…

  • Array.prototype.concat()

    1
    2
    3
    4
    let arr = [1, 3, { username: "kobe" }];
    let arr2 = arr.concat();
    arr2[2].username = "wade";
    console.log(arr); // [ 1, 3, { username: 'wade' } ]
  • Array.prototype.slice(),与 contact 类似

深拷贝的实现方式

  • JSON.parse(JSON.stringify())

    1. 利用 JSON.stringify 将对象转成 JSON 字符串,再用 JSON.parse 把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
    2. 这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于 JSON.stringify 和 JSON.parse 处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为 null)了。
  • jQuery.extend()方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $.extend(deepCopy, target, object1, [objectN]); //第一个参数为true,就是深拷贝

    // 使用
    var $ = require("jquery");
    var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3],
    };
    var obj2 = $.extend(true, {}, obj1);
    console.log(obj1.b.f === obj2.b.f); // false
  • 手写递归方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // 递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
    function deepClone(obj, hash = new WeakMap()) {
    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;
    }
    let obj = { name: 1, address: { x: 100 } };
    obj.o = obj; // 对象存在循环引用的情况
    let d = deepClone(obj);
    obj.address.x = 200;
    console.log(d);