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

JS 中的深拷贝与浅拷贝

前言 最近在写项目的时候涉及到一些父子组件传递个对象或者数组通信啥的,或者是直接复制添加对象啥的,直接使用赋值的时候总会出错。一查原来是浅拷贝的问题,就从网上找了点资料,汇总到这里来了。

1 什么是深拷贝&浅拷贝

见名知义,无论是深拷贝还是浅拷贝,都是 copy 的问题。就是 copy 的时候出现的两种情况。区分起来也挺简单的,举个例子,假设 B 是 A 复制过来的,当我们修改 A 的时候,B 也随之改变了,那么这个就是浅拷贝,那要是 B 没有随 A 一起改变的话,那么这个就深拷贝了。

2 现实场景

首先呢,我们先要明白在 Javascript 中,有 5 种简单数据类型(也称为基本数据类型),分别是 Undefined,Null,String,Number,Boolean,还有 1 种复杂数据类型即 Object,(ES6 新出的 Symbol 数据类型就先不讨论了)

2.1 基本数据类型

对于基本数据类型的复制就谈不上什么深拷贝和浅拷贝了,对于基本数据类型来说,他们的值在栈内存中占据着固定大小的空间,并被保存在栈内存中。假设 变量 b 复制 基本数据类型变量 a,那么 b 会内存中占据自己的空间,和 a 就没啥关系了,大家各管各的,互不干涉。

let a = 2; 
let b = a;
b = 4;
console.log(a); // 2
console.log(b); // 4

2.2 复杂数据类型(Object)

对于对象的话,他是引用类型,复制起来就要区分浅拷贝和深拷贝了,因为 Object 是引用类型,他真正的值保存在堆内存中,他在栈内存存储是变量名和指向该对象值的指针(就是一个地址),如下图所示。
image
所以当我们用平常用一个变量去复制一个 Object 类型的变量的时候,复制的是他的指针地址而已,所以两个变量最终都指向同一个变量,大家要改一起改,这就是浅拷贝啦,如下

let obj1 = {name:'kk',age:12,desc:'源对象'}
let obj2 = obj1;
obj2.desc = '目标对象'
console.log(obj1); //{name:'kk',age:12,desc:'目标对象'} 此处源对象跟着一起变了
console.log(obj2); // {name:'kk',age:12,desc:'目标对象'}

啥,不信?!,那就看图
image
如何
image
但是在我们日常的使用当中,Object 类型的浅拷贝的行为会让我们很迷,我复制这个对象就是想复制他的值而已啦,不要复制人家个值就和他绑到一块了,跟他一起「同生共死」。所以啊,当我们想按照我们复制的想法,就只复制他的值用来自己用,他的是他的,我的是我的,大家井水不犯河水。接下来就要说咋办了。

3 实现对象类型的深拷贝

对于对象的深拷贝,搜集了网上的资料,就有下面三种方法

3.1 slice()&concat()

这个是针对数组的深拷贝,可以通过这两个方法实现对数组的深拷贝,如下

let arr = [1,2,3,4]
let arr2 = arr.slice();
arr[0] = 0;
console.log(arr); //[1,2,3,4]
console.log(arr2); //[1,2,3,4]

concat 同理可得,不过这两个方法有个问题,slice()concat() 方法能够深拷贝的就只有数组的一级属性,但是如果是多维数组的话,那么只有一级属性的值是深拷贝,往下就都是浅拷贝了,如下所示

let arr = [[1,2],2,3,4]
let arr2 = arr.concat();
arr[0][0] = 0;
arr[1] = 1;

console.log(arr); //[[0,2],1,3,4]

console.log(arr2); //[[0,2],2,3,4]
// arr2[1] 没变,但是 arr2[0] 跟着一起改了

image

3.2 JSON 的骚操作

通过 JSON 的 stringify, parase 操作也可以实现对象的深拷贝。

let obj1 = {name:'kk',age:12,desc:'源对象'}
let obj2 = JSON.parase(JSON.stringify(obj1));
obj2.desc = '目标对象';
console.log(obj1); //{name:'kk',age:12,desc:'源对象'} 此处源对象就没有一起变了
console.log(obj2); // {name:'kk',age:12,desc:'目标对象'}

此法数组和对象都可以用。

3.3 自己写一个深拷贝函数

自己动手,丰衣足食

function deepCopy(obj) {
  let newObj = Array.isArray(obj) ? [] : {};
  if (obj && typeof obj === "object") {
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        //判断ojb子元素是否为对象,如果是,递归复制
        if (obj[key] && typeof obj[key] === "object") {
          newObj[key] = deepCopy(obj[key]);
        } else {
          //如果不是,简单复制
          newObj[key] = obj[key];
        }
      }
    }
  }
  return newObj;
}

let a = [1,2,3];
let b = deepCopy(a);
a[0] = 0;
console.log(a); //[0,2,3]
console.log(a); // [1,2,3]

3.4 JQ 的 extend 方法

这个就直接放文档了
$.extend( [deep ], target, object1 [, objectN ] )
deep:如果设为true,则递归合并即深拷贝。
target:待修改对象。
object1:待合并到第一个对象的对象。
objectN:待合并到第一个对象的对象。
使用如下

let a = [1,2,3],
let b = $.extend(true,[],a);
a[0]=1;
console.log(a); // [0,2,3]
console.log(b); // [1,2,3]

以上就是关于 JS 中的深拷贝与浅拷贝的知识和如何进行深拷贝的知识了,如果有错或者有其他方式的话,欢迎在下面留言评论啦

相关文章:

  • 理解React Hooks
  • Django的Rbac介绍3
  • 毫秒级从百亿大表任意维度筛选数据,是怎么做到的...
  • 网上流传的那些关于链表的面试问题
  • JavaScript的使用你知道几种?(上)
  • 根据开始日期和当前日期,获取当前是第几周
  • 服务发现全量配置整理(更新中)
  • MySql版本查看
  • 业务员类别窗体的制作
  • lucene 思维导图,让搜索引擎不再难懂
  • “如何让团队成员获得成长?”四名高段位 CTO 为你解惑
  • 二叉树应用
  • Yii2 RULE 校验器
  • 使用xorm工具,根据数据库自动生成 go 代码
  • 服务端渲染(SSR)
  • [译] 怎样写一个基础的编译器
  • 《剑指offer》分解让复杂问题更简单
  • 07.Android之多媒体问题
  • 2017 前端面试准备 - 收藏集 - 掘金
  • CODING 缺陷管理功能正式开始公测
  • co模块的前端实现
  • ES6 ...操作符
  • JavaScript类型识别
  • leetcode386. Lexicographical Numbers
  • Mysql数据库的条件查询语句
  • Promise初体验
  • spring-boot List转Page
  • Swoft 源码剖析 - 代码自动更新机制
  • vue-cli在webpack的配置文件探究
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • 进程与线程(三)——进程/线程间通信
  • # C++之functional库用法整理
  • #FPGA(基础知识)
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (6)添加vue-cookie
  • (bean配置类的注解开发)学习Spring的第十三天
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (转)scrum常见工具列表
  • (转)winform之ListView
  • .describe() python_Python-Win32com-Excel
  • .NET Remoting学习笔记(三)信道
  • .NET性能优化(文摘)
  • /proc/stat文件详解(翻译)
  • [AIGC] 使用Curl进行网络请求的常见用法
  • [C#]猫叫人醒老鼠跑 C#的委托及事件
  • [dfs] 图案计数
  • [I2C]I2C通信协议详解(二) --- I2C时序及规格指引
  • [iHooya]2023年1月30日作业解析