什么是深拷贝;深拷贝和浅拷贝有什么区别;深拷贝和浅拷贝有哪些方法(详解)

目录

一、为什么要区别深拷贝和浅拷贝

二、浅拷贝

2.1、什么是浅拷贝

2.2、浅拷贝的方法

使用Object.assign()

使用展开运算符(...)

使用数组的slice()方法(仅适用于数组)

2.3、关于赋值运算符(=)

三、深拷贝

3.1、什么是深拷贝

3.2、实现深拷贝的三种常见方法

使用JSON.parse(JSON.stringify(obj))

使用第三方库,如lodash的cloneDeep()

手动实现递归函数

四、总结


一、为什么要区别深拷贝和浅拷贝

        在JavaScript中,对象和数组是引用类型,这意味着当你将它们赋值给一个变量或者作为函数参数传递时,你实际上是在传递一个指向内存中对象的引用,而不是对象本身的副本。这就涉及到了深拷贝和浅拷贝的概念。

二、浅拷贝

2.1、什么是浅拷贝

        浅拷贝只复制对象的第一层属性。也就是说,如果原始对象的属性是基本类型(如数字、字符串、布尔值),那么浅拷贝会复制这些值;但如果属性是引用类型(如数组、对象、函数),那么拷贝的只是引用,而不复制引用指向的内存地址中的数据。

2.2、浅拷贝的方法

使用Object.assign()
let original = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, original);

original.a = 3;
original.b.c = 4;

console.log(shallowCopy.a) // 1 ; original.a是original的第一层属性,值是一个数字,所以浅拷贝到shallowCopy中还是一个数值,不受original影响

console.log(shallowCopy.b.c) // 4 ; original.b.c是original的第二层属性,第一层属性original.b的值是一个引用类型,浅拷贝只会拷贝引用,所以修改original.b.c也会修改shallowCopy.b.c
使用展开运算符(...)
let original = { a: 1, b: { c: 2 } };
let shallowCopy = { ...original }; // 使用展开运算符进行浅拷贝

original.a = 3;
original.b.c = 4;

console.log(shallowCopy.a) // 1 ; original.a是original的第一层属性,值是一个数字,所以浅拷贝到shallowCopy中还是一个数值,不受original影响

console.log(shallowCopy.b.c) // 4 ; original.b.c是original的第二层属性,第一层属性original.b的值是一个引用类型,浅拷贝只会拷贝引用,所以修改original.b.c也会修改shallowCopy.b.c
使用数组的slice()方法(仅适用于数组)
let originalArray = [1, 2, 3, [4, 5]];
let shallowCopyArray = originalArray.slice();

originArray.push(6);
originArray[3].push(5);


console.log(originalArray);   // 输出: [1, 2, 3, [4, 5, 5], 6] 
console.log(shallowCopyArray); // 输出: [1, 2, 3, [4, 5, 5]]



let originalArray = [1, 2, 3, [4, 5]];
let shallowCopyArray = originalArray.slice();
originArray.push(6);
originArray[3].push(5);
originArray[3] = [4, 5];

console.log(originalArray);   // 输出: [1, 2, 3, [4, 5], 6] 
console.log(shallowCopyArray); // 输出: [1, 2, 3, [4, 5, 5]] 因为 [4, 5]是一个新数组,是将新的引用赋给了originArray[3],并不会直接改变shallowCopyArray[3]的引用

2.3、关于赋值运算符(=)

        在JavaScript中,赋值运算符(=)本身并不执行浅拷贝或深拷贝。赋值运算符只是将一个值或一个引用赋给一个变量。如果赋值的是一个基本数据类型(如数字、字符串、布尔值),那么赋值运算符会复制这个值。如果赋值的是一个引用类型(如对象、数组、函数),那么赋值运算符会复制这个引用,而不是引用指向的对象本身。

        当使用赋值运算符来处理对象和数组时,没有创建一个新的对象/数组,两个变量指向的是同一个对象/数组,所以不管修改哪一个对象/数组的任何一层的任何一个数据,另外一个都会随之改变。(连浅层/第一层的数值都没有拷贝,完全依赖引用)

let a = 10; // a是基本数据类型
let b = a;  // b复制了a的值,a和b现在是两个独立的数字

console.log(a === b); // true,因为它们都是数字10

let obj1 = { key: 'value' };
let obj2 = obj1; // obj2复制了obj1的引用

obj2.key = 'new value'; // 修改obj2的key属性

console.log(obj1.key); // 输出 'new value',因为obj1和obj2引用的是同一个对象
console.log(obj1 === obj2); // true,因为它们引用的是同一个对象


let obj3 = { key: 'value' , deep:{a:5 , b:6}};
let obj4 = obj1; // obj2复制了obj1的引用
obj3.deep.a = 6

console.log(obj4.deep.a); //6

三、深拷贝

3.1、什么是深拷贝

        深拷贝会创建一个全新的对象,并且递归地复制所有属性,使得原始对象和拷贝对象完全独立,互不影响。深拷贝后的对象中所有的属性都是新的引用,即使属性是引用类型,也会被完全复制。

        实现深拷贝的方法相对复杂,因为需要递归地复制对象的所有层级。以下是一些常见的实现方式

3.2、实现深拷贝的三种常见方法

使用JSON.parse(JSON.stringify(obj))

        这是最简单的一个方法,实际开发中经常会用到,不过虽然简单但有局限性,不能处理函数、undefined、循环引用、特殊对象(如Date、RegExp)等。

let original = { a: 1, b: { c: 2 } };
let deepCopy = JSON.parse(JSON.stringify(original));

使用第三方库,如lodash的cloneDeep()
let original = { a: 1, b: { c: 2 } };
let deepCopy = JSON.parse(JSON.stringify(original));
手动实现递归函数

        实现深拷贝的递归函数需要考虑多种情况,包括但不限于:

  • 检测对象是否为基本类型。
  • 处理循环引用。
  • 复制数组和对象。
  • 处理特殊对象(如Date、RegExp)。

        举个例子:

function deepCopy(obj, hash = new WeakMap()) {
    if (obj === null) return null;
    if (typeof obj !== 'object') return obj;
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    if (hash.has(obj)) return hash.get(obj);

    let result;
    if (Array.isArray(obj)) {
        result = [];
        hash.set(obj, result);
        for (let i = 0; i < obj.length; i++) {
            result[i] = deepCopy(obj[i], hash);
        }
    } else {
        result = Object.create(Object.getPrototypeOf(obj));
        hash.set(obj, result);
        for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
                result[key] = deepCopy(obj[key], hash);
            }
        }
    }
    return result;
}

四、总结

        浅拷贝适用于只复制对象的第一层属性,且这些属性不是引用类型。深拷贝适用于需要完全独立的副本,包括对象和数组的嵌套结构。选择哪种拷贝方式取决于你的具体需求和场景。

        意犹未尽?更多精彩前端好文请关注:各种前端问题的技巧和解决方案

        博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

深拷贝浅拷贝是在对象复制过程中常用的两种方式,它们的区别在于复制后的对象与原始对象之间的关系。 浅拷贝(Shallow Copy)是创建一个新对象,该对象与原始对象的元素是对原始对象中元素的引用。换言之,复制后的对象与原始对象共享内部对象的引用。当修改原始对象中的可变元素时,这些修改也会反映在复制后的对象中。 深拷贝(Deep Copy)则是创建一个全新的独立对象,复制后的对象与原始对象完全独立,互不影响。深拷贝会递归地复制原始对象及其内部所有可变对象,而不仅仅是引用。 举个例子来说明: ```python import copy # 原始对象 original_list = [1, 2, [3, 4]] # 浅拷贝 shallow_copy = copy.copy(original_list) # 深拷贝 deep_copy = copy.deepcopy(original_list) # 修改原始对象中的可变元素 original_list[0] = 10 original_list[2].append(5) print(original_list) # 输出: [10, 2, [3, 4, 5]] print(shallow_copy) # 输出: [10, 2, [3, 4, 5]] print(deep_copy) # 输出: [1, 2, [3, 4]] ``` 在上面的例子中,原始对象是一个列表 `original_list`,其中包含一个整数和一个列表。通过浅拷贝 `copy.copy()` 和深拷贝 `copy.deepcopy()` 分别创建了复制对象 `shallow_copy` 和 `deep_copy`。 当我们修改原始对象的第一个元素和第三个元素的子列表时,可以看到浅拷贝对象也受到了影响,而深拷贝对象保持不变。 因此,在需要创建独立的对象副本且避免修改原始对象时,深拷贝是更安全和可靠的选择,而浅拷贝则更适合在一些特定场景下节省内存或共享数据。
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值