深拷贝的几种方式

文章介绍了JavaScript中手动递归实现的深拷贝方法,以及使用JSON.stringify/parse、Object.assign和lodash.cloneDeep进行深拷贝的局限性。着重讨论了这些方法在处理复杂对象和特殊类型时的适用性。
摘要由CSDN通过智能技术生成

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.stringify和JSON.parse进行深拷贝有以下局限性

1.无法处理循环引用:如果对象中存在循环引用,则在使用JSON.stringify时会导致错误,无法完成深拷贝。

2.丢失特定类型信息:通过JSON.stringify和JSON.parse进行序列化和反序列化会导致一些特定类型的信息丢失,例如日期对象被转换成字符串,正则表达式对象被转换成空对象。

3.无法拷贝函数和原型链:JSON.stringify和JSON.parse只能处理JSON支持的数据类型,无法拷贝函数和原型链。

4.对象中存在undefined值时会被忽略:JSON.stringify会将对象中的undefined值转换为null,而JSON.parse会忽略null值,导致无法还原原始的undefined值。

5.性能较低:相比其他深拷贝方法,使用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 } }
在编程中,对象和数组的深拷贝涉及到数据结构的副本创建,确保新创建的对象与原对象独立,即使原对象发生改变也不会影响副本。这里有几种常见的深拷贝方式: 1. **浅拷贝(Shallow Copy)**: 对于简单类型(如基本数据类型)或值类型,拷贝的是它们的值。对于复杂类型如数组或对象,浅拷贝实际上是创建了一个引用,两个变量指向的是同一个内存地址。这意味着对其中一个的修改会影响到另一个。 2. **深拷贝(Deep Copy)**: - **复制数组**:对于数组,可以逐个元素地进行深拷贝,创建一个新的数组并填充每个元素的深拷贝。例如,在JavaScript中可以使用`JSON.parse(JSON.stringify(array))`来实现。 - **复制对象**: a. 使用构造函数:创建新对象,然后递归遍历原对象,为每个属性创建新的值,而不是引用。例如在JavaScript中,`function deepCopy(obj) { return new obj.constructor(obj); }`。 b. 使用`Object.assign()`和扩展运算符...:结合使用,创建一个新的对象,并将所有属性从源对象复制过来,但不会创建循环引用。但是这种方法不适用于原型链中的对象。 c. 库方法:有些编程语言有现成的库函数或模块可以直接完成深拷贝操作,比如Python的`copy.deepcopy()`。 3. **深度克隆(Deep Cloning)**: 术语"深度克隆"通常用于描述深度复制的过程,特别是在讨论复杂的数据结构,如树、图或对象树时。 **相关问题**: 1. 浅拷贝和深拷贝的主要区别是什么? 2. 如何在JavaScript中避免对象属性的引用冲突进行深拷贝? 3. 如果数组中的元素是对象,应该如何正确地进行深拷贝
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值