loading

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

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

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

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

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

2.2、浅拷贝的方法使用Object.assign()代码语言:javascript复制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使用展开运算符(...)代码语言:javascript复制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()方法(仅适用于数组)代码语言:javascript复制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中,赋值运算符(=)本身并不执行浅拷贝或深拷贝。赋值运算符只是将一个值或一个引用赋给一个变量。如果赋值的是一个基本数据类型(如数字、字符串、布尔值),那么赋值运算符会复制这个值。如果赋值的是一个引用类型(如对象、数组、函数),那么赋值运算符会复制这个引用,而不是引用指向的对象本身。

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

代码语言: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)等。

代码语言:javascript复制let original = { a: 1, b: { c: 2 } };

let deepCopy = JSON.parse(JSON.stringify(original));使用第三方库,如lodash的cloneDeep()代码语言:javascript复制let original = { a: 1, b: { c: 2 } };

let deepCopy = JSON.parse(JSON.stringify(original));手动实现递归函数 实现深拷贝的递归函数需要考虑多种情况,包括但不限于:

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

代码语言:javascript复制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;

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

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

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