js 深度克隆

深度克隆(Deep Clone)是指复制一个对象或数组及其所有嵌套结构的副本,使得克隆后的对象与原对象完全独立。JavaScript 提供了一些方法实现深度克隆,但每种方法有其优缺点。

1. 常用方法

1.1 使用 JSON.parseJSON.stringify

这种方法最简单,但有局限性。

const obj = { a: 1, b: { c: 2 } };
const clone = JSON.parse(JSON.stringify(obj));

console.log(clone); // { a: 1, b: { c: 2 } }
console.log(clone === obj); // false
console.log(clone.b === obj.b); // false
优点
  • 简单易用。
  • 适用于纯 JSON 格式的数据。
缺点
  • 不支持函数、undefinedSymbol、循环引用等。
  • 日期对象会变成字符串,RegExp 对象会丢失。

1.2 使用 structuredClone

structuredClone 是一种内置的深拷贝方法,支持复杂数据结构。

const obj = { a: 1, b: { c: 2 }, d: new Date() };
const clone = structuredClone(obj);

console.log(clone); // { a: 1, b: { c: 2 }, d: Date }
console.log(clone === obj); // false
console.log(clone.b === obj.b); // false
优点
  • 支持更多类型(如 DateRegExpMapSet 等)。
  • 处理循环引用。
缺点
  • 不支持旧版本浏览器。
1.3 使用递归实现深度克隆

通过递归遍历对象和数组,手动实现深拷贝。

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  if (obj instanceof Date) {
    return new Date(obj);
  }

  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }

  const clone = Array.isArray(obj) ? [] : {};

  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key]);
    }
  }

  return clone;
}

const obj = { a: 1, b: { c: 2 }, d: new Date() };
const clone = deepClone(obj);

console.log(clone); // { a: 1, b: { c: 2 }, d: Date }
console.log(clone === obj); // false
console.log(clone.b === obj.b); // false
优点
  • 完全控制克隆逻辑。
  • 支持特定类型的处理。
缺点
  • 不支持循环引用,需额外处理。

2. 循环引用处理

对于有循环引用的对象,需要使用 WeakMap 来避免递归陷入死循环。

function deepClone (value, cache = new WeakMap ()) {

    if (typeof value !== "object" || value === null) {
        return value;
    }

    if (cache.has(value)) {
        return cache.get(value);
    }

    const result = Array.isArray(value) ? [] : {};

    Object.setPrototypeOf(result, Object.getPrototypeOf(value));

    cache.set(value, result);

    for (i in value) {
        if (value.hasOwnProperty(i)) {
            result[i] = deepClone(value[i], cache);
        }
    }

    return result;
}

const obj = { a: 1 };
obj.b = obj; // 循环引用

let clone = deepClone(obj);
console.log(clone); // { a: 1, b: [Circular] }


const Person = function(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.a = 100;

clone = new Person('a', 12)

console.log(clone);

3. 方法对比

方法支持类型处理循环引用性能易用性
JSON.parse/stringify仅支持 JSON 格式简单
structuredClone支持大部分类型简单
手动递归可定制支持类型否(需扩展)中等
Lodash支持复杂结构和循环引用简单

推荐

  • 数据结构简单:使用 JSON.parseJSON.stringify
  • 现代浏览器:使用 structuredClone
  • 复杂场景:使用 Lodash 或手动实现带循环引用处理的深拷贝函数。
<think>好的,我现在需要处理用户关于JavaScript深度克隆的问题。用户明确想要实现方法或代码示例。首先,我得回忆一下JavaScript深度克隆的常见方法,以及相关的注意事项。 根据用户提供的引用,有几种不同的方法:使用第三方库如Lodash的cloneDeep,jQuery的extend方法,原生JavaScript的递归实现,以及新的structuredCloneAPI。我需要把这些方法整理清楚,确保信息准确。首先,Lodash的cloneDeep是一个可靠的选择,用户可能已经熟悉这个库,所以应该提到。然后,jQuery的extend方法虽然现在可能不太常用,但仍有用户在使用旧项目,所以也需要包括。递归实现是核心,需要详细解释代码,同时指出它的局限性,比如无法处理特殊对象类型或循环引用。接下来是structuredClone,这是比较新的原生方法,支持很多数据类型,但需要注意浏览器兼容性。用户可能不知道这个新API,所以值得强调。 然后,我需要比较不同方法的优缺点,帮助用户根据具体情况选择。比如第三方库功能强大但增加依赖,原生方法可能更轻量但有限制。还要检查用户提供的引用中的代码示例是否正确。例如,递归实现是否处理了数组和对象,是否考虑了原型链,以及是否有正确处理基本类型的情况。例如引用3中的代码使用了Object.setPrototypeOf来保持原型一致,这点很重要。另外,用户可能关心循环引用的问题,但根据引用中的代码,似乎递归实现没有处理这一点,而Lodash可以处理。需要指出这一点,避免用户在使用自定义递归函数时遇到问题。最后,生成相关问题需要考虑用户可能的后续疑问,比如如何处理特殊对象、性能比较、循环引用等。确保问题覆盖用户可能感兴趣的方向。 整理回答结构时,要分点清晰,每种方法单独说明,给出示例代码,并注明优缺点。引用对应的引用编号,确保正确归属。同时,语言要简洁明了,符合中文表达习惯。检查是否有遗漏的方法,比如JSON.parse和JSON.stringify,虽然这不安全,但有时候用户会提到,但根据引用中没有提到,可能用户不需要这种方法,或者需要避免推荐,所以可以简要提及但不推荐。最后确保所有LaTeX格式正确,但问题中没有数学公式,可能不需要使用。但根据系统指令,行内数学表达式需要用$...$,但这里可能用不上,所以可能不需要处理。但需要检查是否有其他格式问题,比如代码块是否正确使用三个反引号,引用标识是否正确添加。 总结步骤:介绍深度克隆概念,分方法详细说明,代码示例,优缺点,引用标注,相关问题生成。确保每个部分准确无误,参考用户提供的引用内容,并正确引用。</think>### JavaScript深度克隆实现方法详解 #### 一、使用第三方库(推荐) 1. **Lodash的cloneDeep方法** 可处理循环引用、Map/Set等复杂类型,兼容性最佳[^1]: ```javascript import cloneDeep from 'lodash/cloneDeep'; const clonedObj = cloneDeep(originalObj); ``` 2. **jQuery的.extend()方法** 适合旧项目,需注意jQuery依赖[^2]: ```javascript const clonedObj = $.extend(true, {}, originalObj); ``` #### 二、原生JavaScript实现 1. **递归实现(基础版)** 处理普通对象和数组,但无法克隆函数、特殊对象(如Date)和循环引用[^3]: ```javascript function deepClone(value) { if (typeof value !== "object" || value === null) return value; const result = Array.isArray(value) ? [] : {}; Object.setPrototypeOf(result, Object.getPrototypeOf(value)); for (const key in value) { if (value.hasOwnProperty(key)) { result[key] = deepClone(value[key]); } } return result; } ``` 2. **structuredClone API(现代浏览器)** 原生支持克隆大多数数据类型(包括Blob、RegExp),但无法克隆函数和DOM节点[^4]: ```javascript const clonedObj = structuredClone(originalObj); ``` #### 三、对比与选择 | 方法 | 优点 | 缺点 | |--------------------|-------------------------------|------------------------------| | Lodash.cloneDeep | 功能最全面,支持循环引用 | 需引入第三方库 | | 递归实现 | 无依赖,可自定义扩展 | 无法处理特殊对象/循环引用 | | structuredClone | 原生支持,性能较好 | 兼容性要求高(Chrome 98+) | #### 四、注意事项 - **循环引用**需特殊处理(如WeakMap记录已克隆对象) - **原型链**需通过`Object.setPrototypeOf`保持一致性[^3] - **特殊对象**(如Map/Set)需单独实现克隆逻辑
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值