深拷贝、浅拷贝、区别?如何实现?

13 阅读2分钟

前言

  • 基本数据存储在中(先进后出)
  • 引用数据指针存储在中,数据存储在
  • 深拷贝和浅拷贝是针引用(复杂)数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝

浅拷贝

  • 浅拷贝 基本类型的数据 - 拷贝的就是基本类型的
  • 浅拷贝 引用类型的数据 - 共享内存地址

javaScript中存在浅拷贝的现象有:

  • 使用扩展运算符实现的数组和对象的复制
  • Object.assign(obj1, obj2)
  • Array.prototype.slice() 语法: arr.slice(start, end)
  • Array.prototype.concat() 语法:arr= arr1.concat(arr2, arr3, arr4)
  • lodash 里面的 _.clone

浅拷贝代码实现:

function shallowClone(obj) {
    const newObj = {};
    for(let prop in obj) {
        if(obj.hasOwnProperty(prop)){
            // 未判断obj[prop]的数据类型 仅仅拷贝了一层
            newObj[prop] = obj[prop];
        }
    }
    return newObj;
}

深拷贝

深拷贝就是把一个对象,从内存中完整的拷贝出来,从堆内存中开辟了新区域,用来存新对象,并且修改新对象不会影响原对象

常见的深拷贝:

  • _.cloneDeep() 语法: obj2=_.cloneDeep(bj1)
  • jQuery.extend() 语法:obj2 = $.extend(true, {}, obj1)
  • JSON.parse(JSON.stringify())语法:obj2=JSON.parse(JSON.stringify(obj1))
  • 手写递归

1、_.cloneDeep()

const _ = require('lodash');
const obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false

2、jQuery.extend()

const $ = require('jquery');
const obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
const obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false

3、JSON.parse(JSON.stringify())

const obj2=JSON.parse(JSON.stringify(obj1))

但是这种方式存在弊端,会忽略 undefined symbol 函数

const obj = {
    name: 'A',
    name1: undefined,
    name3: function() {},
    name4: Symbol('A')
}

const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}

4、循环递归

// WeakMap是JavaScript中的一种内置数据结构,它是一种特殊的映射(Map),用于存储键值对
// 与普通的Map不同,WeakMap的键必须是对象
function deepClone(obj, hash = new WeakMap()) {
    // 如果是null 或者 undeind 不进行拷贝操作
    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;
}

总结

浅拷贝和深拷贝都创建出一个新的对象,但在复制对象属性的时候,行为就不一样。浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,修改对象属性会影响原对象。

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

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