2023-06-06 01:57:47
深拷贝和浅拷贝的核心区别在于对对象内部引用类型的处理方式:深拷贝会递归复制所有嵌套对象,生成完全独立的副本;浅拷贝仅复制对象第一层的属性,嵌套对象仍共享引用。 以下是具体分析:
一、基础概念解析浅拷贝
定义:仅复制对象的第一层属性,若属性值为引用类型(如对象、数组),则复制的是引用地址而非实际数据。
特点:
修改拷贝对象的基本类型属性(如数字、字符串)不会影响原对象。
修改拷贝对象的引用类型属性(如嵌套对象、数组)会同步影响原对象,因为两者指向同一内存地址。
实现方式:
JavaScript中可通过Object.assign()、展开运算符{...obj}或数组的slice()、concat()实现。
示例:
const original = { a: 1, b: { c: 2 } };const shallowCopy = { ...original };shallowCopy.b.c = 3; // 原对象original.b.c也会变为3深拷贝
定义:递归复制对象的所有层级,包括嵌套的引用类型,生成完全独立的副本。
特点:
修改拷贝对象的任何层级属性均不会影响原对象。
需要处理循环引用等特殊情况,否则可能导致栈溢出。
实现方式:
递归实现:手动遍历对象属性,对每个引用类型属性递归调用深拷贝。
JSON方法:通过JSON.parse(JSON.stringify(obj))实现,但会忽略函数、Symbol、undefined等特殊类型。
第三方库:如Lodash的_.cloneDeep()。
示例:
function deepClone(obj) { if (typeof obj !== 'object' || obj === null) return obj; const clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { clone[key] = deepClone(obj[key]); // 递归复制 } return clone;}const original = { a: 1, b: { c: 2 } };const deepCopy = deepClone(original);deepCopy.b.c = 3; // 原对象original.b.c仍为2“深拷贝通过递归实现赋值”的原理
递归会逐层检查对象属性,若属性为引用类型,则继续向下复制直到所有层级均为基本类型。
例如,复制{ a: { b: { c: 1 } } }时,递归会先复制外层对象,再复制a属性指向的对象,最后复制b属性指向的对象。
“for循环数组相当于深拷贝”的误区
错误原因:
若数组元素为基本类型(如数字、字符串),for循环复制确实会生成独立副本,因为基本类型按值传递。
但若数组元素为对象或数组,for循环仅复制引用地址,仍属于浅拷贝。
示例:
const originalArr = [{ a: 1 }, { b: 2 }];const copiedArr = [];for (let i = 0; i < originalArr.length; i++) { copiedArr.push(originalArr[i]); // 仅复制引用}copiedArr[0].a = 3; // 原数组originalArr[0].a也会变为3“变量赋值与拷贝无关”
基本类型(如let a = 1; let b = a)的赋值是按值传递,生成独立副本,但这不是拷贝的范畴。
拷贝仅针对对象或数组等引用类型,因为它们的赋值是按引用传递。
内存分配差异
浅拷贝:原对象与拷贝对象共享嵌套对象的内存地址,修改会相互影响。
深拷贝:所有层级均分配新内存,修改互不影响。
内存图示例:
浅拷贝:原对象和拷贝对象的第一层属性指向不同内存,但嵌套对象指向同一内存。
深拷贝:所有层级属性均指向新分配的内存。
性能权衡
浅拷贝速度快,适合无嵌套对象的场景。
深拷贝速度慢(需递归遍历),但能保证数据完全独立。
特殊场景处理
循环引用:对象属性间接或直接引用自身,需通过WeakMap记录已复制对象避免无限递归。
非JSON安全类型:如函数、Date对象,需自定义深拷贝逻辑。