热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaScript深拷贝和浅拷贝

一、前言hello,大家好~,本文主要介绍在JavaScript中什么是深拷贝和浅拷贝,以及如何实现一个对象的深拷贝。二、随处可见的“赋值”在JavaScript

Javascript 深拷贝和浅拷贝

一、前言

hello,大家好~ ,本文主要介绍在 Javascript 中什么是深拷贝和浅拷贝,以及如何实现一个对象的深拷贝。

二、随处可见的 “赋值”

在 Javascript 中我们最常见的操作之一是将一个变量的值赋值给另一个变量,这个过程我们也可以称为 “拷贝” 一份变量的值给另一个变量。

2.1 基本数据类型的赋值操作

在涉及到基本数据类型(string、Boolean、number...)赋值操作的时候,原始变量值被复制给另外一个变量。我们来考虑下下面这段代码:我们将值为 10 的变量 x 赋值给变量 y,此时 x 和 y 已经失去“联系”了,所以改变 x 的值不会影响 y 的值,最后输出 20 和 10。


2.2 引用数据类型的赋值操作

与基本数据类型赋值相反,引用数据类型(Object、Array...)在赋值的时候传递的只是一个 引用 (原始值和拷贝赋值后的值指向同一块内存空间) ,所以当被复制出来的对象值改变的时候,原始的对象值也会跟着改变,这种现象被称为 浅拷贝

对象赋值案例: 将 user 对象赋值给 user2 变量,修改 user2 属性值后 user 属性值也会跟着变,两者的值始终保持同步,这便是浅拷贝带来的 副作用 ,拷贝出来的值和原始值始终保持着关联。基本数据类型的赋值没有这种现象,所以说 浅拷贝深拷贝 是相对引用数据类型而言的。


三、实现对象的深拷贝

通过以上的介绍我们知道什么是 浅拷贝 以及浅拷贝带来的 副作用 ,为了消除这种“副作用”我们需要对对象进行 深拷贝 。下面将介绍一些对象拷贝的方法,它们各有优缺点。

  • "=" 号直接赋值

通过等号(=)将原始值赋值给一个新的值,优点是简便快捷,缺点 是只能进行 浅拷贝

let obj = {name:"zjl712", age:18}
let obj2 = obj;
console.log(obj == obj2) //true
  • Object.assign()

通过 Object 的 assign 方法可以对对象进行深拷贝,缺点是不能对含有多层嵌套结构的对象进行深拷贝。

深拷贝单层结构的对象,改变拷贝对象的值不会改变原始对象的值。


尝试拷贝多层嵌套结构对象,改变拷贝对象内嵌套的对象值(obj2.local.nickname)后,原始对象嵌套的值也会跟着改变。改变拷贝对象非嵌套的值,原始对象的值不会跟着变。所以 Object.assign() 只能实现部分深拷贝。


  • JSON.stringify() 和 JSON.parse()

JSON.stringify() 方法接收一个对象作为参数,将对象转为 JSON 字符串。JSON.parse() 接收一个 JSON 字符串,将该字符串转为一个对象。结合这两个方法可以对一个对象进行深拷贝。

结合以上两个方法对对象进行深拷贝

let obj = {name: "zjl712",age: 18}
let obj2 = JSON.parse(JSON.stringify(obj))
console.log(obj == obj2); // false
obj2.name = "zjl"
console.log(obj, obj2);

当对象属性值含有 函数(function) 时,且含有嵌套对象时。拷贝的值 属性值为函数的属性会丢失 。优点是能对多层嵌套对象进行深拷贝。

let obj = {
    name: "zjl712", 
    age: 18, 
    getName:function(){
        return this.name
    },
    local: {
        name: "shanghai",
        nickname: "modu",
    }
}
let obj2 = JSON.parse(JSON.stringify(obj))
console.log(obj == obj2); // false
obj2.local.nickname = "魔都"
obj2.name = "zjl"
console.log(obj, obj2);
console.log(obj.local == obj2.local) // false

  • 对象扩展符(...)

使用 ES6 的扩展操作符,使用三个点(...)将原始对象的值拷贝给另一个对象。然而三点扩展符和 Object.assign() 很相似,扩展符不能对多层嵌套对象进行深拷贝。

let obj = {
    name: "zjl712", 
    age: 18, 
    getName:function(){
        return this.name
    },
    local: {
        name: "shanghai",
        nickname: "modu",
    }
}
let obj2 = {...obj}
console.log(obj == obj2); // false
obj2.local.nickname = "魔都"
obj2.name = "zjl"
console.log(obj, obj2);

  • 递归的方式实现对象的深拷贝

实现含有嵌套结构、函数对象的深拷贝以上方法都无能为力,我们可以用递归的方法完成对象的深拷贝。缺点是对复杂的对象(Buffer、Date 等实例对象)无法实现深拷贝

function myDeepClone(obj){
    let clone;
    // 排除非引用类型数据
    if(obj == null || typeof obj != "object") return obj;
    if(Array.isArray(obj)){
        // obj 是数组
        clOne= new obj.constructor(obj.length)
        obj.forEach((value, index) => {
            clone[index] = typeof value === "object"?myDeepClone(value):value
        })
    }else{
        // 浅拷贝一份原始数据
        clOne= Object.assign({}, obj)
        // 递归 clone 内的每一个属性值
        Object.keys(clone).forEach(key => {
            clone[key] = typeof obj[key] === "object"?myDeepClone(obj[key]):obj[key]
        })
    }
    return clone;
}

以上递归代码有相似代码(两个 forEach 内的代码),可简化为以下代码:

function myDeepClone(obj){
    let clone;
    if(obj == null || typeof obj != "object") return obj;
    clOne= Object.assign({}, obj)
    Object.keys(clone).forEach(key => {
        clone[key] = typeof obj[key] === "object"?myDeepClone(obj[key]):obj[key]
    })
    if(Array.isArray(obj)){
        clone.length = obj.length
        clOne= Array.from(clone)
    }
    return clone
}
  • 以上的自定义递归方法不够全面,但对于基本的对象( {} )和数组( [] )深拷贝还是够用的,要想实现复杂对象的深拷贝可以使用 lodashcloneDeep 方法,这个方法实现深拷贝是比较完美的。
// 安装 lodash :npm i lodash
// 使用
let lang = require("lodash/lang")
let clOne= lang.cloneDeep(obj)

原文链接:https://www.cnblogs.com/zjl-712/archive/2022/11/16/16894695.html


推荐阅读
  • 本文介绍了一个React Native新手在尝试将数据发布到服务器时遇到的问题,以及他的React Native代码和服务器端代码。他使用fetch方法将数据发送到服务器,但无法在服务器端读取/获取发布的数据。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • React项目中运用React技巧解决实际问题的总结
    本文总结了在React项目中如何运用React技巧解决一些实际问题,包括取消请求和页面卸载的关联,利用useEffect和AbortController等技术实现请求的取消。文章中的代码是简化后的例子,但思想是相通的。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • 本文介绍了在使用vue和webpack进行异步组件按需加载时可能出现的报错问题,并提供了解决方法。同时还解答了关于局部注册组件和v-if指令的相关问题。 ... [详细]
  • 本文介绍了在wepy中运用小顺序页面受权的计划,包含了用户点击作废后的从新受权计划。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 一、什么是闭包?有什么作用什么是闭包闭包是定义在一个函数内部的函数,它可以访问父级函数的内部变量。当一个闭包被创建时,会关联一个作用域—— ... [详细]
author-avatar
水平蓝精灵天堂_678
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有