JS :深拷贝解析与实现(附structuredClone语法测试)

浅拷贝简介

深拷贝是创建一个新对象,这个新对象包含原对象所有属性的全新拷贝,无论是基本数据类型还是引用类型的数据都会被完全复制一份,新旧对象间不存在任何关联,彼此独立。

前言

OK,最近又又又在学习JS的过程中遇到了有意思的问题,就是如何对一个复杂类型也就是对象类型实现深拷贝 ,浅拷贝大家都会,直接创建变量赋值就行,可是深拷贝呢?

我想到用for in 语法遍历对象属性,然后对新对象进行赋值 。

vbnet 复制代码
function shallowCopy(obj) {
    let newObj = {};
    for (let key in obj) {
        // key是不是obj显示具有的属性
        if (obj.hasOwnProperty(key)) {
                newObj[key] = obj[key];
        }
    }
    return newObj;
}

ok,问题是如果出现下面这种情况则无法满足,因为obj的子元素person任然是对象类型,直接赋值传进来的是地址,就不满足深拷贝了。那么接下来我们就要展开来详细谈论一下深拷贝。

css 复制代码
var obj = {
    person:{
        name :'柳如烟',
        age: 18
    }
}

深拷贝实现方式

以下是我所知道的深拷贝方式

  • JSON.parse(JSON.stringify(obj))

  • structuredClone

  • 手写deepClone

    接下来我将分别通过相同的测试集来讲述一下他们的限制与功能。

yaml 复制代码
let obj = {
    a: 1,
    b: { n: 2 },
    c: 'cc',
    d: true,
    e: undefined,
    f: null,
    g: function () { },
    h: Symbol(1),
    i: 123n,
    j: [1, 2, 3, 4]
}

JSON.parse(JSON.stringify(obj))

JSON.parse(JSON.stringify(obj)) 是JavaScript中实现深拷贝的一种简便方法,这种用法源自于JavaScript语言本身对JSON对象的支持以及这两个方法的特性。

  • JSON.stringify: 这个方法用于将JavaScript对象转换成JSON字符串。它能够序列化JavaScript对象,包括对象字面量、数组等,将其转换为字符串格式,以便于存储或在网络上传输。

  • JSON.parse : 则是JSON.stringify的逆操作,用于将JSON格式的字符串解析回JavaScript值(通常是对象或数组)。这个过程称为反序列化。

代码实现

ini 复制代码
let newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);

测试结果

bigInt类型不能加入测试集否则会直接报错,我们可以发现,undefined、function、Symbol都是无法进行Clone

structuredClone(obj)

structuredClone 是一个相对较新的JavaScript原生函数,用于创建一个对象或值的深拷贝。该方法在 ECMAScript 2021(ES12)规范中被引入,相较于传统的JSON.parse(JSON.stringify(obj))方法,它具有更多的优势和更广泛的适用性。

代码实现

ini 复制代码
const newObj = structuredClone(obj);
console.log(newObj);

测试结果

这里的functionSymbol类型都不能进行深拷贝,因为该语法不支持functionSymbol类型的深拷贝。事实上,structuredClone支持更多JSON无法进行clone的数据类型这里就不一一展开讨论,structuredClone功能是要比JSON更加强大,但是因为是新的语法所以兼容性是稍逊一筹。

自定义深拷贝

递归思想

自定义深拷贝通常通过递归遍历对象的所有属性,对于基本类型直接复制,而对于引用类型则继续递归调用深拷贝函数,确保完全独立地复制所有层级的数据结构。

代码实现

scss 复制代码
function shallowCopy(obj) {
    let newObj = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            // 判断一下obj[key]是不是一个对象
            if (obj[key] instanceof Object) {
            //如果是对象则进行递归操作
                newObj[key] = shallowCopy(obj[key]);
            } else {
            // 基本类型则直接赋值
                newObj[key] = obj[key];
            }
        }
    }
    return newObj;
}

测试结果

基本全部都能进行深拷贝,唯一不同的就是Symbol类型其实这里并不是深拷贝。因为Symbol是原始数据类型之一,和字符串、数字、布尔值等一样,它们在赋值时都是按值传递的。但是,对于Symbol而言,这个"值"更像是一个引用标识符,它指向在全局Symbol注册表中的一个唯一 Symbol 值。所以这里两个对象指向的Symbol其实是同一个值。

结语

面对深拷贝的局限性,开发者还需根据实际情况采取额外措施或寻找更全面的解决方案,如利用第三方库lodash提供的_.cloneDeep方法,来增强深拷贝功能的全面性和实用性。

本次分享到此结束,如果有疑惑或者本文出现错误的地方欢迎大家在评论区指出,喜欢的话就点个关注吧。>_<

相关推荐
前端一小卒10 小时前
我用 TypeScript 从零手写了一个 Claude Code,然后发现它的核心只有 30 行
前端·agent
Waay10 小时前
面试口述版:个人对 Prometheus 完整理解
运维·学习·云原生·面试·职场和发展·kubernetes·prometheus
铁皮饭盒11 小时前
用 Bun.cron 定时 7 月 7 日,为啥? 看图1
javascript
大圣编程12 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang12 小时前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆13 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜13 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
负责的蛋挞14 小时前
异步HttpModule的实现方式
java·服务器·前端
黄敬峰15 小时前
🚀 从 Prompt 到 Harness:AI 编程的下半场,我们如何给大模型套上“挽具”?
面试
用户8524950718415 小时前
手搓自然语义搜索:从传统匹配到向量化,理解 RAG 的第一步
面试