跳至主要內容

深拷贝浅拷贝的区别?如何实现一个深拷贝?


深拷贝浅拷贝的区别?如何实现一个深拷贝?

一、数据类型存储

在 JavaScript 中,数据类型分为两类:

  • 基本类型(Primitive Types):包括 stringnumberbooleanundefinedsymbolbigintnull。基本类型的数据存储在栈内存中,值直接保存在变量中。
  • 引用类型(Reference Types):包括 objectarrayfunction 等。引用类型的数据存储在堆内存中,变量中存储的是指向堆内存的引用。

二、浅拷贝

浅拷贝是对对象进行复制,但只会复制对象的第一层属性。如果对象的属性值是引用类型,拷贝的是引用,而不是对象本身。因此,浅拷贝后的对象和原对象共享对引用类型属性的修改。

浅拷贝常见的方法:

  • Object.assign()
  • Array.prototype.slice()
  • 扩展运算符(...

示例:

function shallowClone(obj) {
	const newObj = {};
	for (let prop in obj) {
		if (obj.hasOwnProperty(prop)) {
			newObj[prop] = obj[prop];
		}
	}
	return newObj;
}

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = shallowClone(obj1);
obj2.b.c = 3;

console.log(obj1.b.c); // 3 (浅拷贝共享引用)
console.log(obj2.b.c); // 3

常见的浅拷贝方法:

  • Object.assign()
const obj = { a: 1, b: [2, 3] };
const clone = Object.assign({}, obj);
clone.b[0] = 4;
console.log(obj.b[0]); // 4
  • 使用 slice()concat() 对数组进行浅拷贝
const arr = [1, 2, 3];
const shallowArr = arr.slice();
shallowArr[0] = 0;
console.log(arr[0]); // 1
console.log(shallowArr[0]); // 0
  • 使用扩展运算符(...
const arr = [1, 2, 3];
const shallowArr = [...arr];
shallowArr[0] = 0;
console.log(arr[0]); // 1
console.log(shallowArr[0]); // 0

三、深拷贝

深拷贝是递归地复制对象的所有层级,包括引用类型的属性,使得原对象和新对象在内存中是完全独立的。深拷贝常见方法:

  • 使用 JSON.stringify()JSON.parse() 进行深拷贝
  • 使用 lodash.cloneDeep()
  • 手动递归实现深拷贝

示例:

// 使用 JSON.stringify 和 JSON.parse 深拷贝
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.c = 3;

console.log(obj1.b.c); // 2 (深拷贝独立)
console.log(obj2.b.c); // 3

JSON.stringify()JSON.parse() 方法有局限,无法处理 undefinedsymbol函数循环引用 等。

手动实现深拷贝

function deepClone(obj, hash = new WeakMap()) {
	if (obj === null) return obj; // 处理 null
	if (obj instanceof Date) return new Date(obj); // 处理 Date 对象
	if (obj instanceof RegExp) return new RegExp(obj); // 处理 RegExp 对象
	if (typeof obj !== 'object') return obj; // 基本类型直接返回

	if (hash.get(obj)) return hash.get(obj); // 防止循环引用

	let cloneObj = new obj.constructor(); // 创建新对象
	hash.set(obj, cloneObj); // 存储对象引用以避免循环引用
	for (let key in obj) {
		if (obj.hasOwnProperty(key)) {
			cloneObj[key] = deepClone(obj[key], hash); // 递归拷贝
		}
	}
	return cloneObj;
}

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = deepClone(obj1);
obj2.b.c = 3;

console.log(obj1.b.c); // 2 (深拷贝独立)
console.log(obj2.b.c); // 3

四、浅拷贝与深拷贝的区别

  • 浅拷贝:只拷贝对象的第一层,如果属性值是引用类型,拷贝的是地址(引用)。因此,修改引用类型的属性会影响原对象。

  • 深拷贝:递归拷贝对象的所有层级,即使属性是引用类型,也会创建一个新的对象副本,因此原对象和拷贝对象互不影响。

浅拷贝示例:

const obj1 = { a: 1, arr: [1, 2, 3] };
const obj2 = shallowClone(obj1);
obj2.arr[0] = 9;
console.log(obj1.arr[0]); // 9 (浅拷贝共享引用)

深拷贝示例:

const obj1 = { a: 1, arr: [1, 2, 3] };
const obj2 = deepClone(obj1);
obj2.arr[0] = 9;
console.log(obj1.arr[0]); // 1 (深拷贝独立)