アシマ / Dream Blue
851 字
4 分钟
深拷贝与浅拷贝
数据类型
JavaScript 中的数据类型可以分为两大类:基本数据类型和引用数据类型。
- 基本数据类型:包括
Number
、String
、Boolean
、null
、undefined
和Symbol
。这些类型的值是不可变的,直接存储在栈内存中。 - 引用数据类型:包括
Object
、Array
、Function
等。这些类型的值是可变的,存储在堆内存中,变量存储的是指向堆内存中实际数据的引用。
浅拷贝
浅拷贝是指创建一个新对象,这个新对象有着原始对象属性值的一份精确拷贝。如果属性值是基本数据类型,拷贝的是值本身;如果属性值是引用数据类型,拷贝的是内存地址(引用),因此两个对象会共享同一个引用对象。
实现浅拷贝的方法
- Object.assign()
const original = { a: 1, b: { c: 2 } };const shallowCopy = Object.assign({}, original);shallowCopy.a = 10;shallowCopy.b.c = 3;console.log(original); // { a: 1, b: { c: 3 } }console.log(shallowCopy); // { a: 10, b: { c: 3 } }
- 扩展运算符(Spread Operator)
const original = { a: 1, b: { c: 2 } };const shallowCopy = { ...original };shallowCopy.a = 10;shallowCopy.b.c = 3;console.log(original); // { a: 1, b: { c: 3 } }console.log(shallowCopy); // { a: 10, b: { c: 3 } }
- Array.prototype.slice(), Array.prototype.concat()(用于数组)
const originalArray = [1, 2, { a: 3 }];const shallowCopyArray = originalArray.slice();const anotherShallowCopyArray = originalArray.concat();shallowCopyArray[0] = 10;shallowCopyArray[2].a = 4;console.log(originalArray); // [1, 2, { a: 4 }]console.log(shallowCopyArray); // [10, 2, { a: 4 }]console.log(anotherShallowCopyArray); // [1, 2, { a: 4 }]
- 手动实现浅拷贝函数
function shallowClone(obj) { if (obj === null || typeof obj !== "object") { return obj; // 处理基本数据类型 } const clone = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = obj[key]; } } return clone;}
深拷贝
深拷贝是指创建一个新对象,这个新对象与原始对象完全独立,且包含原始对象所有属性的递归拷贝。如果属性值是基本数据类型,拷贝的是值本身;如果属性值是引用数据类型,拷贝的是该引用对象的一个全新副本。
实现深拷贝的方法
- JSON.parse() 和 JSON.stringify()
const original = { a: 1, b: { c: 2 } };const deepCopy = JSON.parse(JSON.stringify(original));deepCopy.b.c = 3;console.log(original); // { a: 1, b: { c: 2 } }console.log(deepCopy); // { a: 1, b: { c: 3 } }
这种方法简单但有局限性,不能拷贝 function
、undefined
、symbol
。
- 递归函数
function deepClone(obj, hash = new WeakMap()) { if (obj === null || 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 clone = new obj.constructor(); hash.set(obj, clone); for (const key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key], hash); } } return clone;}
区别
- 内存分配:浅拷贝只复制对象的第一层属性,引用类型的属性仍然指向同一个内存地址;深拷贝则是递归复制所有层级的属性,完全独立。
- 性能:浅拷贝通常比深拷贝更快,因为它只复制一层属性;深拷贝由于需要递归处理,性能开销较大。
- 适用场景:浅拷贝适用于对象属性较为简单且不包含嵌套对象的情况;深拷贝适用于需要完全独立的对象,尤其是包含嵌套对象的情况。
- 循环引用:浅拷贝不会处理循环引用的问题,而深拷贝需要特别处理循环引用以避免无限递归。