文章目录
- 1. 手动递归拷贝
- 2. JSON序列化和反序列化
- 3. Object.assign()方法
- 4. 使用第三方库
在JavaScript中,实现深拷贝的方式有以下几种:
1. 手动递归拷贝
通过递归遍历对象的属性,并逐个进行复制。例如:
function deepCopy(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } let copy = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { copy[key] = deepCopy(obj[key]); } } return copy; } let obj = { a: 1, b: { c: 2 } }; let copy = deepCopy(obj); console.log(copy); // { a: 1, b: { c: 2 } }
复制
以上是比较简单的深拷贝,只处理了基本类型和数组、对象
下面是个比较全面的深拷贝,考虑到了正则,日期等
function deepCopy(obj) { // 如果是null或者undefined直接返回 if (obj === null || typeof obj !== 'object') { return obj; } // 处理日期对象 if (obj instanceof Date) { const copy = new Date(); copy.setTime(obj.getTime()); return copy; } // 处理正则表达式对象 if (obj instanceof RegExp) { const flags = []; if (obj.global) flags.push('g'); if (obj.ignoreCase) flags.push('i'); if (obj.multiline) flags.push('m'); return new RegExp(obj.source, flags.join('')); } // 处理数组 if (Array.isArray(obj)) { const copy = []; for (let i = 0; i < obj.length; i++) { copy[i] = deepCopy(obj[i]); } return copy; } // 处理对象 const copy = {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { copy[key] = deepCopy(obj[key]); } } return copy; }
复制
示例用法:
const obj1 = { name: 'John', age: 30, hobbies: ['reading', 'coding'], address: { city: 'New York', country: 'USA' }, birthday: new Date('1990-01-01'), regex: /hello/g }; const obj2 = deepCopy(obj1); console.log(obj2); // 修改obj2的值,不会影响到obj1 obj2.name = 'Tom'; obj2.hobbies.push('playing'); obj2.address.city = 'London'; obj2.birthday.setFullYear(1995); console.log(obj1); console.log(obj2);
复制
输出结果:
{ name: 'John', age: 30, hobbies: [ 'reading', 'coding' ], address: { city: 'New York', country: 'USA' }, birthday: 1990-01-01T00:00:00.000Z, regex: /hello/g } { name: 'John', age: 30, hobbies: [ 'reading', 'coding' ], address: { city: 'New York', country: 'USA' }, birthday: 1990-01-01T00:00:00.000Z, regex: /hello/g } { name: 'Tom', age: 30, hobbies: [ 'reading', 'coding', 'playing' ], address: { city: 'London', country: 'USA' }, birthday: 1995-01-01T00:00:00.000Z, regex: /hello/g }
复制
2. JSON序列化和反序列化
通过使用JSON.stringify()和JSON.parse()方法将对象转为字符串,再转回对象,实现深拷贝。但该方法不能处理函数、循环引用等特殊情况。例如:
let obj = { a: 1, b: { c: 2 } }; let copy = JSON.parse(JSON.stringify(obj)); console.log(copy); // { a: 1, b: { c: 2 } }
复制
json序列化的局限性
使用JSON.stringify和JSON.parse进行深拷贝有以下局限性:
-
无法处理循环引用:如果对象中存在循环引用,则在使用JSON.stringify时会导致错误,无法完成深拷贝。
-
丢失特定类型信息:通过JSON.stringify和JSON.parse进行序列化和反序列化会导致一些特定类型的信息丢失,例如日期对象被转换成字符串,正则表达式对象被转换成空对象。
-
无法拷贝函数和原型链:JSON.stringify和JSON.parse只能处理JSON支持的数据类型,无法拷贝函数和原型链。
-
对象中存在undefined值时会被忽略:JSON.stringify会将对象中的undefined值转换为null,而JSON.parse会忽略null值,导致无法还原原始的undefined值。
-
性能较低:相比其他深拷贝方法,使用JSON.stringify和JSON.parse进行深拷贝的性能较低,尤其是在处理大型对象或嵌套深的对象时,会消耗较多的时间和内存。
综上所述,尽管JSON.stringify和JSON.parse可以完成一些简单对象的深拷贝,但在处理复杂或特殊类型的对象时存在一定的局限性。更好的方法是使用其他深拷贝技术,如递归复制、Object.assign()或第三方库(如lodash的cloneDeep方法),以满足更多复杂的深拷贝需求。
3. Object.assign()方法
此方法可以将多个对象的属性合并到一个目标对象中,从而实现深拷贝。但对于嵌套的对象或数组,仍然是浅拷贝。例如:
let obj = { a: 1, b: { c: 2 } }; let copy = Object.assign({}, obj); console.log(copy); // { a: 1, b: { c: 2 } }
复制
4. 使用第三方库
如Lodash中的cloneDeep()
方法,它提供了更强大和灵活的深拷贝功能。例如:
const _ = require('lodash'); let obj = { a: 1, b: { c: 2 } }; let copy = _.cloneDeep(obj); console.log(copy); // { a: 1, b: { c: 2 } }
复制