当前位置: 首页 > article >正文

Javascript深拷贝常见实现方法

深拷贝与浅拷贝

在JavaScript中,深拷贝是一个常见的需求,特别是在处理复杂数据结构(如对象、数组等)时,需要确保原始数据不被修改。下面通过表格形式列出几种常见的深拷贝方法,并简要说明其优缺点。

方法优点缺点
JSON 方法1. 简单快捷,一行代码即可实现。1. 无法处理函数、undefinedSymbolRegExp等特殊对象。
 2. 适用于大多数基本数据类型和简单对象结构。2. 可能会丢失对象的原型链。
  3. 递归对象(对象中包含自身引用)会导致无限循环。
手动递归1. 可以精确控制拷贝过程,包括如何处理特殊对象。1. 实现复杂,需要手动处理各种数据类型和特殊情况。
 2. 可以保留对象的原型链(如果需要)。2. 性能可能不如其他自动化方法,特别是在处理大型对象时。
  • JSON 方法(如JSON.parse(JSON.stringify(obj)))虽然简单,但因其局限性,并不适用于所有场景。
  • 手动递归方法虽然灵活,但实现起来较为复杂,特别是在处理复杂数据结构时。

递归实现深拷贝

bd3024d1502d4d8395e2a50ac20e0c4a.png

当然,下面是一个利用递归实现的JavaScript简单深拷贝函数的示例。这个函数将处理对象、数组以及它们的嵌套结构,但为了简化,我们不会处理像DateRegExpMapSet等特殊对象类型,也不会处理函数对象(因为函数在JavaScript中是按引用传递的,且深拷贝函数对象通常不是必要的或期望的)。

function deepClone(obj) {  
    // 对于非对象或null,直接返回  
    if (obj === null || typeof obj !== 'object') {  
        return obj;  
    }  
  
    // 初始化拷贝目标  
    let cloneObj;  
  
    // 处理数组  
    if (Array.isArray(obj)) {  
        cloneObj = [];  
        obj.forEach(item => {  
            cloneObj.push(deepClone(item)); // 递归拷贝数组中的每一项  
        });  
    }  
    // 处理对象  
    else {  
        cloneObj = {};  
        Object.keys(obj).forEach(key => {  
            cloneObj[key] = deepClone(obj[key]); // 递归拷贝对象的每一个属性值  
        });  
    }  
  
    return cloneObj;  
}  
  
// 示例使用  
const original = {  
    a: 1,  
    b: { c: 2, d: [3, 4] },  
    e: [5, 6, { f: 7 }]  
};  
  
const cloned = deepClone(original);  
console.log(cloned); // 输出深拷贝后的对象  
console.log(cloned.b.d === original.b.d); // false,说明数组也是深拷贝的  
console.log(cloned.e[2].f === original.e[2].f); // false,说明嵌套对象也是深拷贝的

在JavaScript中,实现一个利用递归的深拷贝函数可以处理大多数数据类型,包括对象、数组、以及它们的嵌套结构。下面是一个简单的实现示例:

function deepClone(obj, hash = new WeakMap()) {  
    // 对于非对象或null,直接返回  
    if (obj === null || typeof obj !== 'object') {  
        return obj;  
    }  
  
    // 如果对象已经存在于哈希表中,则直接返回其引用  
    if (hash.has(obj)) {  
        return hash.get(obj);  
    }  
  
    let cloneObj;  
  
    // 处理数组  
    if (Array.isArray(obj)) {  
        cloneObj = [];  
        hash.set(obj, cloneObj);  
        obj.forEach(item => {  
            cloneObj.push(deepClone(item, hash));  
        });  
    }  
    // 处理Date对象  
    else if (obj instanceof Date) {  
        cloneObj = new Date(obj);  
    }  
    // 处理RegExp对象  
    else if (obj instanceof RegExp) {  
        // 注意:RegExp的flags属性在ES6中引入  
        const flags = obj.flags ? `${obj.source}/${obj.flags}` : `${obj.source}/${(obj.global ? 'g' : '') + (obj.ignoreCase ? 'i' : '') + (obj.multiline ? 'm' : '') + (obj.unicode ? 'u' : '') + (obj.sticky ? 'y' : '')}`;  
        cloneObj = new RegExp(flags, obj.flags);  
    }  
    // 处理普通对象  
    else {  
        cloneObj = {};  
        hash.set(obj, cloneObj);  
        Object.keys(obj).forEach(key => {  
            cloneObj[key] = deepClone(obj[key], hash);  
        });  
  
        // 如果需要保持原型链,可以添加以下代码  
        // Object.setPrototypeOf(cloneObj, Object.getPrototypeOf(obj));  
    }  
  
    return cloneObj;  
}  
  
// 示例使用  
const original = {  
    a: 1,  
    b: { c: 2, d: [3, 4] },  
    c: new Date(),  
    d: /abc/gi,  
    e: function() { console.log('hello'); },  
    f: {  
        g: original // 循环引用  
    }  
};  
  
const cloned = deepClone(original);  
console.log(cloned);  
console.log(cloned.f.g === cloned); // true,因为处理了循环引用

注意:

  1. 这个函数使用了WeakMap来存储已经拷贝过的对象,以避免循环引用导致的无限递归。
  2. 对于特殊对象如DateRegExp,我们分别进行了特殊处理。
  3. 函数默认不保持原型的继承关系,如果需要,可以取消注释Object.setPrototypeOf相关的代码行。
  4. 函数能够处理函数对象,但请注意,函数对象在JavaScript中是按引用传递的,即使进行了深拷贝,函数本身还是同一个函数,只是函数对象作为属性被复制到了新的对象中。
  5. 对于更复杂的对象(如MapSetBlobFile等),你可能需要添加额外的逻辑来支持它们的深拷贝。

 


http://www.kler.cn/a/280601.html

相关文章:

  • 【Qt】项目打包发布
  • k8s POD控制器
  • 【Java设计模式】非循环访问者模式:简化对象交互
  • Python算法工程师面试整理-数据结构
  • sql中exist和in的区别
  • 了解一点电池的工作原理,让它们更好地为我们工作。【手机充电小技巧】(影响电池寿命的主要因素:过充、过放以及高温)
  • Java中Map集合的高级应用与实战技巧
  • docker手动部署django项目Dockerfile编排-后端发布
  • SpringBoot项目集成数据脱敏(密码加密)功能
  • 【设计模式之建造者模式——自行车加工】
  • 2024谷歌开发者大会分享
  • Android手机安装Kali系统并配置ddns-go
  • leetcode 1137 第n个泰波那契数
  • Eureka原理实践
  • Ubuntu中PCL、Eigen、ROS、Ceres、VScode相关操作,安装,卸载,文件存储位置基础合集
  • 怎么检测电脑的RAM?丨什么是RAM?
  • 【8G 显存玩转书生大模型 Demo】
  • 深度学习100问2-分布式假设在自然语言处理中的应用场景有哪些
  • 数据结构(邓俊辉)学习笔记】串 01——ADT
  • idea import配置