当前位置: 首页 > 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)
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • C# 免费离线人脸识别 2.0 Demo
  • canvas绘制圆角头像
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • JS实现简单的MVC模式开发小游戏
  • linux学习笔记
  • Python实现BT种子转化为磁力链接【实战】
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • spring学习第二天
  • vuex 学习笔记 01
  • win10下安装mysql5.7
  • 闭包,sync使用细节
  • 如何实现 font-size 的响应式
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 问题之ssh中Host key verification failed的解决
  • 正则表达式-基础知识Review
  • ​flutter 代码混淆
  • ![CDATA[ ]] 是什么东东
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • #if #elif #endif
  • $.ajax()方法详解
  • $forceUpdate()函数
  • (2)STL算法之元素计数
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (java)关于Thread的挂起和恢复
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (二)WCF的Binding模型
  • (二)斐波那契Fabonacci函数
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (转)Android学习笔记 --- android任务栈和启动模式
  • (转)scrum常见工具列表
  • (转)创业的注意事项
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .net 8 发布了,试下微软最近强推的MAUI
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .net 流——流的类型体系简单介绍