面试官:请你手写一个深拷贝

前言:

在之前的文章中提到过关于深拷贝和浅拷贝的概念,给各位详细介绍了一下二者的区别。但是在关于深拷贝的部分,通过JSON.parse和JSON.stringify是无法对null,symbol以及bigint等类型的值进行拷贝甚至报错的。正常情况下,会使用Lodash库去实现深拷贝,但是很显然,在各大卷王的激情贡献下,面试官们已经完全不满足于这个回答了

正文:

递归深拷贝:

在手写前还是要简单说明一下,深拷贝或浅拷贝这一概念仅针对于引用类型的值(比如object,array),而基础类型则无一例外全部都是深拷贝,所以在手写的深拷贝函数的时候,最先需要的就是判断数据类型,是基础类型则直接返回否则再进行深拷贝操作

js 复制代码
function deepCopy(obj) {
    if (obj === null) return null; // 处理null值
    if (!(obj instanceof Object)) return obj // 基本数据类型直接返回
}

随后,判断拷贝的值是数组还是对象并设置返回值

js 复制代码
    let copyObj = Array.isArray(obj) ? [] : {}

接下来就是通过for-in方法去遍历拷贝的值,并且对每个值都要判断是否是自身属性。需要注意一点的是,每个元素都有可能是引用类型,切记不可直接赋值,否则还是浅拷贝。所以应该通过循环调用,也就是递归去不断地往深处寻找,直到找到原始类型的值,就是递归出口

js 复制代码
 for (key in obj) {
        if (obj[key] instanceof Object) {
            copyObj[key] = deepCopy(obj[key])
        } else {
            copyObj[key] = obj[key]
        }
    }

最后,将拷贝后的值返回

js 复制代码
function deepCopy(obj) {
    if (obj === null) return null; // 处理null值
    if (!(obj instanceof Object)) return obj // 基本数据类型直接返回

    let copyObj = Array.isArray(obj) ? [] : {}

    for (key in obj) {
        if (obj[key] instanceof Object) {
            copyObj[key] = deepCopy(obj[key])
        } else {
            copyObj[key] = obj[key]
        }
    }
    return copyObj
}

非递归深拷贝:

当然,如果对象叠加层数非常多,而且还无法对递归进行尾调用处理以至于通过递归深拷贝会造成爆栈(真是多到离谱),那么我们也可以利用weakMap数据类型,通过非递归的方法去进行深拷贝

  1. 首先,还是一样的进行类型判断,然后根据拷贝值的类型去设置返回体
js 复制代码
function deepCopyII(obj, hash = new WeakMap()) {
    if (obj === null) return null; // 处理null值
    if (typeof obj !== 'object') return obj; // 基本数据类型直接返回

    // 检查是否已经拷贝过
    if (hash.has(obj)) return hash.get(obj);

    let cloneObj= Array.isArray(obj) ? [] : {}
}
  1. 随后,设置一个队列,把要拷贝的值和返回值塞进一个数组里面,然后再把数组放进队列
js 复制代码
// 使用队列来存储需要拷贝的对象属性
    let queue = [[obj, cloneObj]];

当队列内部还有值的时候,首先解构出原数据和拷贝数据

js 复制代码
 while (queue.length > 0) {
        let [original, copy] = queue.shift();
  1. 然后通过for-in方法去遍历原数据,这里还是一样,要判断是否自身属性以及原数据是否依旧是引用类型。如果是原始类型,则将原数据的值直接赋值给返回值的对应键或下标。否则就将这个引用类型的值和一个新的对象(数组)塞进新数组并重新放回队列。这样就能保证在不使用递归的前提下能一层层找到原数据的最底层
js 复制代码
for (let key in original) {
            if (original.hasOwnProperty(key)) { // 确保是自身属性
                if (typeof original[key] === 'object' && original[key] !== null) {
                    // 如果属性值也是对象,则递归拷贝
                    if (!hash.has(original[key])) {
                        // 创建新的拷贝
                        let newObj = Array.isArray(original[key]) ? [] : {};
                        hash.set(original[key], newObj);
                        queue.push([original[key], newObj]);
                    }
                    // 将原始对象的属性拷贝到新对象
                    copy[key] = hash.get(original[key]);
                } else {
                    // 如果不是对象,直接赋值
                    copy[key] = original[key];
                }

最后,返回拷贝的值

js 复制代码
function deepCopyII(obj, hash = new WeakMap()) {
    if (obj === null) return null; // 处理null值
    if (typeof obj !== 'object') return obj; // 基本数据类型直接返回

    // 检查是否已经拷贝过
    if (hash.has(obj)) return hash.get(obj);

    let cloneObj;
    if (Array.isArray(obj)) {
        cloneObj = [];
    } else {
        cloneObj = {};
    }

    // 将原始对象和它的拷贝关联起来
    hash.set(obj, cloneObj);

    // 使用队列来存储需要拷贝的对象属性
    let queue = [[obj, cloneObj]];

    while (queue.length > 0) {
        let [original, copy] = queue.shift();

        for (let key in original) {
            if (original.hasOwnProperty(key)) { // 确保是自身属性
                if (typeof original[key] === 'object' && original[key] !== null) {
                    // 如果属性值也是对象,则递归拷贝
                    if (!hash.has(original[key])) {
                        // 创建新的拷贝
                        let newObj = Array.isArray(original[key]) ? [] : {};
                        hash.set(original[key], newObj);
                        queue.push([original[key], newObj]);
                    }
                    // 将原始对象的属性拷贝到新对象
                    copy[key] = hash.get(original[key]);
                } else {
                    // 如果不是对象,直接赋值
                    copy[key] = original[key];
                }
            }
        }
    }

    return cloneObj;
}

总结:

通过这两种方法,我们不仅能够有效地实现深拷贝,还能根据不同的需求和场景选择最适合的实现方式。无论是递归还是非递归深拷贝,都能够确保原始对象和拷贝对象之间是完全独立的,从而避免了修改一个对象时影响另一个对象的问题。最后祝各位看官老爷0 waring(s),0 error(s)。

相关推荐
_Legend_King4 分钟前
vue3 + elementPlus 日期时间选择器禁用未来及过去时间
javascript·vue.js·elementui
爱吃青椒不爱吃西红柿‍️5 分钟前
华为ASP与CSP是什么?
服务器·前端·数据库
余生H6 分钟前
transformer.js(三):底层架构及性能优化指南
javascript·深度学习·架构·transformer
一棵开花的树,枝芽无限靠近你8 分钟前
【PPTist】添加PPT模版
前端·学习·编辑器·html
陈王卜11 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
景天科技苑19 分钟前
【vue3+vite】新一代vue脚手架工具vite,助力前端开发更快捷更高效
前端·javascript·vue.js·vite·vue项目·脚手架工具
SameX21 分钟前
HarmonyOS Next 安全生态构建与展望
前端·harmonyos
石小石Orz28 分钟前
Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~
javascript·人工智能·算法
小行星12530 分钟前
前端预览pdf文件流
前端·javascript·vue.js
join831 分钟前
解决vue-pdf的签章不显示问题
javascript·vue.js·pdf