手写一个深拷贝的方法,支持所有数据类型

function deepCopy(obj) {
    // 处理基本数据类型和函数
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    // 处理日期对象
    if (obj instanceof Date) {
        return new Date(obj.getTime());
    }

    // 处理正则表达式对象
    if (obj instanceof RegExp) {
        return new RegExp(obj);
    }

    // 处理数组
    if (Array.isArray(obj)) {
        const newArray = [];
        for (let i = 0; i < obj.length; i++) {
            newArray[i] = deepCopy(obj[i]);
        }
        return newArray;
    }

    // 处理对象
    const newObj = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = deepCopy(obj[key]);
        }
    }
    return newObj;
}

// 示例用法:
const obj1 = {
    name: 'John',
    age: 30,
    address: {
        city: 'New York',
        country: 'USA'
    },
    hobbies: ['reading', 'sports']
};

const obj2 = deepCopy(obj1);

console.log(obj2); // 输出与 obj1 结构相同的对象

解释:

  1. 基本数据类型和函数的处理:

    如果 obj 是 null 或者不是对象(如基本数据类型和函数),直接返回 obj,因为它们是不可变的。
  2. 特殊对象类型的处理:

    如果 obj 是 Date 对象,通过 new Date(obj.getTime()) 复制日期。
    如果 obj 是 RegExp 对象,通过 new RegExp(obj) 复制正则表达式。
     
  3. 数组的处理:

    如果 obj 是数组,创建一个新数组 newArray,并递归地将每个元素拷贝到 newArray 中。
     
  4. 对象的处理:

    如果 obj 是普通对象,创建一个空对象 newObj,并递归地复制每个属性到 newObj 中。
     
  5. 递归调用:

    • 使用递归调用 deepCopy 方法,确保所有嵌套的对象和数组都被正确地复制。

      这个方法确保了对所有数据类型的支持,并能够完整地复制对象及其嵌套的所有属性和元素。

<think>好的,用户想知道如何手动实现深拷贝以及背后的原理。首先,我得回忆一下深拷贝和浅拷贝的区别。浅拷贝只复制对象的顶层属性,如果属性是引用类型,拷贝后的对象和原对象会共享这些引用,修改其中一个会影响另一个。而深拷贝是递归复制所有层级的对象,完全独立,互不影响。用户需要的是手写深拷贝方法,所以得考虑如何递归处理各种数据类型。 接下来,我需要确定哪些数据类型需要特殊处理。比如基本类型(number、string、boolean等)可以直接返回,而对象和数组需要递归处理。此外,还要处理循环引用的情况,避免无限递归导致栈溢出。这时候可能需要一个WeakMap来存储已经拷贝过的对象,每次拷贝前先检查是否已经存在,如果存在就直接返回存储的副本,防止循环引用的问题。 然后,考虑如何判断数据类型。可以用typeof判断基本类型,但要注意null的情况,因为typeof null返回的是'object',所以需要单独处理。对于对象,需要区分普通对象、数组、日期、正则等内置对象,因为这些对象的拷贝方式可能不同。比如,日期对象可以通过new Date(value)来拷贝,正则表达式可以通过new RegExp(source, flags)来复制。 接下来,编写递归函数。函数的大致步骤是:先处理基本类型和null,然后处理特殊对象(如Date、RegExp),接着处理数组和普通对象。对于数组,遍历每个元素,递归拷贝;对于对象,遍历所有属性,同样递归拷贝每个属性值。同时,使用WeakMap记录已经拷贝过的对象,遇到循环引用时直接返回已拷贝的副本。 用户可能还关心如何验证深拷贝的正确性。可以在示例中创建包含嵌套对象和循环引用的结构,然后修改原对象,检查拷贝后的对象是否受到影响。如果深拷贝成功,修改原对象不会影响拷贝后的对象。 最后,要注意代码的健壮性,处理各种边界情况,比如函数、Symbol等。不过用户可能主要关注对象和数组的深拷贝,所以示例中可以暂时忽略这些复杂情况,或者简单处理,比如直接返回函数本身,或者抛出错误。 现在,把这些思路整理成代码示例,并解释每一步的原理,确保用户能够理解递归过程和循环引用的处理方式。同时,引用相关的资料,比如提到JSON方法的局限性,为什么需要手动实现深拷贝,以及WeakMap的作用,这样回答会更全面。</think># 深拷贝实现原理与手写示例 ## 一、实现原理 1. **递归遍历**:通过递归遍历对象所有层级的属性,直到遇到基本数据类型(数字、字符串、布尔值等)为止[^1] 2. **类型判别**:需处理数组、日期、正则等特殊对象类型,例如日期对象需通过`new Date(value)`重建 3. **循环引用处理**:使用`WeakMap`存储已拷贝对象,解决对象内部循环引用导致的无限递归问题 4. **原型链保留**:通过`Object.create(Object.getPrototypeOf(obj))`保持原型链特性 ## 二、手写实现代码 ```javascript function deepClone(target, map = new WeakMap()) { // 处理基础类型和null if (target === null || typeof target !== 'object') return target // 处理循环引用 if (map.has(target)) return map.get(target) // 处理特殊对象类型 if (target instanceof Date) return new Date(target) if (target instanceof RegExp) return new RegExp(target) // 创建新对象并记录映射关系 const cloneObj = Array.isArray(target) ? [] : Object.create(Object.getPrototypeOf(target)) map.set(target, cloneObj) // 递归拷贝属性 for (let key in target) { if (target.hasOwnProperty(key)) { cloneObj[key] = deepClone(target[key], map) } } return cloneObj } ``` ## 三、验证示例 ```javascript const obj = { a: 1, b: [2, { c: 3 }], d: new Date(), e: /regexp/gi } obj.self = obj // 创建循环引用 const cloned = deepClone(obj) console.log(cloned.b !== obj.b) // true console.log(cloned.d !== obj.d) // true console.log(cloned.e !== obj.e) // true console.log(cloned.self === cloned) // true ``` ## 四、实现要点 1. **WeakMap优势**:相比普通Map,WeakMap使用弱引用,不会阻止垃圾回收,避免内存泄漏[^2] 2. **原型链处理**:`Object.create()`方法比直接`{}`创建更准确保留原型信息 3. **属性过滤**:通过`hasOwnProperty`过滤原型链上的属性 4. **特殊对象处理**:需要单独处理Date、RegExp等内置对象才能正确拷贝
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值