一、自我理解

简单来讲就是:深拷贝层层拷贝,浅拷贝只拷贝第一层

  • 在深拷贝中,新对象中的更改不会影响原对象,而在浅拷贝中,新对象中的更改,原对象中也会跟着改。

  • 在深拷贝中,原对象与新对象不共享相同的属性,而在浅拷贝中,它们具有相同的属性。

举个栗子:存在A和B两个数据,假设B复制了A,当修改A时,如果B跟着A变化了,则是浅拷贝;如果B不受A的改变而改变,则是深拷贝

let A = [0,1,2]
let B = A
console.log(A === B);
A[0] = 3
console.log(A,B);

结果如下:

这就是一个简单的浅拷贝例子,虽然B复制了A,但是修改了A,B却跟着A变化了?

二、数据存储形式

通过上述栗子,我们就需要了解数据类型的存储方式了,如果还没有了解数据类型的小伙伴欢迎阅读 JS中8种数据类型

  • 基本数据类型:number,string,boolean,null,undefined,symbol(es6),还有谷歌67版本中的BigInt(任意精度整数)
  • 引用数据类型:Object【Object是个大类,function函数、array数组、date日期...等都归属于Object】

(1)基本数据类型存储于栈中

变量名和值都存储与栈中

每当声明一个变量,就会开辟一个新的内存空间来存储值,例如:let  a = 1

// 声明一个变量
let a = 1;

当b复制了a后,因为b也是一个变量,所以栈内存会再开辟一个新的内存空间:

// 声明一个变量
let a = 1;
let b = a; //b变量复制a变量
console.log(a,b); //1 1

这就一目了然了,a和b两个变量归属于不同的内存空间,所以当你修改其中一个值,另外一个值不会受其影响!

// 声明一个变量
let a = 1;
let b = a; //b变量复制a变量
console.log(a,b); //1 1
// 修改a的值不影响b
a = 123;
console.log(a,b); //123 1

(2)引用数据类型存储与堆中

变量名存储在栈中,值存在于堆中,这时栈内存中会提供一个指针用于指向堆内存中的值

当我们声明一个引用数据类型时,会提供一个指针指向堆内存中的值:

// 声明一个引用类型的值
let a = [0,1,2,3,4]

当b复制了a后,只是复制了当前栈内存中提供的指针,指向同一个堆内存中的值,并不是复制了堆内存中的值:

let a = [0,1,2,3,4]
let b = a //复制的是一个指针,而不是堆中的值

所以当我们进行a[0]=1 时,导致指针所指向的堆内存中的值发生了改变,而a和b是同一个指针,指向同一个堆内存地址,所以b的值也会受影响,这就是浅拷贝

// 声明一个引用类型的值
let a = [0,1,2,3,4];
let b = a; //复制的是一个指针,而不是堆中的值
console.log(a === b);
a[0] = 1; //改变a数组值,b数组跟着改变(浅拷贝)
console.log(a,b);

所以,我们需要在堆内存中新开辟一个内存专门接收存储b的值,道理与基本数据类型一样,这样就可以实现深拷贝(修改一个值不会影响另一个值的变化):

三、怎样实现深拷贝?

(1)借助JSON对象的parse和stringify

parse()方法用于将一个字符串解析为json对象

stringify()方法用于将一个对象解析为字符串

// 浅拷贝
let arr = [1,"hello",{name:"张三",age:21}]
let copyArr = arr
console.log(arr === copyArr); //true
arr[1] = "您好"
console.log("浅拷贝",arr, copyArr);

// JSON对象方法实现深拷贝
let arr = [1,"hello",{name:"张三",age:21}]
let newArr = JSON.parse(JSON.stringify(arr)) //将arr数组转换为字符串,再作为参数转化为对象
arr[1] = "您好"
arr[2].name = "法外狂徒"
console.log("深拷贝",arr, newArr);

原理是利用JSON.stringify将对象转成JSON字符串,再用JSON.parse把字符串解析成对象,这样在堆中开辟了新的内存,实现深拷贝

这种写法非常简单,而且可以应对大部分的应用场景,但是它还是有很大缺陷的,比如拷贝其他引用类型、拷贝函数、循环引用等情况

(2)手写递归

递归方法实现深度克隆原理:遍历对象、数组...直到里边都是基本数据类型,然后再去复制,就是深度拷贝

  • 如果是原始类型,无需继续拷贝,直接返回
  • 如果是引用类型,创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性执行深拷贝后依次添加到新对象上
// 手写递归实现深拷贝
function clone(target) {
if (typeof target === 'object') {
// 创建一个新对象
let cloneTarget = {};
// 遍历需要克隆的对象
for (const key in target) {
// 将需要克隆对象的属性执行深拷贝后依次添加到新对象上
cloneTarget[key] = clone(target[key]);
}
// 返回克隆对象
return cloneTarget;
} else {
return target;
}
};
// 模拟拷贝对象
const person = {
name: "李小白",
age: 18,
job: {
name:"web前端",
salary:"15k",
address:"成都"
}
};
// 将需要克隆的对象传递给clone函数作为参数,接收一个克隆对象作为返回结果
let newTarget = clone(person)
//修改原对象属性
person["name"] = "李大白"
person["job"]["salary"] = "25k"
console.log(person,newTarget); //实现深拷贝

利用递归可以实现深度拷贝,但是有局限性,例如数组还没考虑进去!

想要兼容数组其实很简单,直接判断一下参数是不是数组,然后利用三木运算决定是创建一个新对象还是一个新数组:

function clone(target) {
if (typeof target === 'object') {
let isArr = Array.isArray(target)
// 判断传入的参数是数组吗 ? 是则赋值[] : 否则赋值{}
let cloneTarget = isArr ? [] : {};
// 遍历需要克隆的对象
for (const key in target) {
// 将需要克隆对象的属性执行深拷贝后依次添加到新对象上
cloneTarget[key] = clone(target[key]);
}
// 返回克隆对象
return cloneTarget;
} else {
return target;
}
};

(3)JQuery中extend方法

在jQuery中提供一个$extend()来实现深拷贝

语法:

$.extend( [deep ], target, object1 [, objectN ] )
  • deep 表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
  • target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
  • object1  objectN可选。 Object类型 第一个以及第N个被合并的对象。
let arr = ["李小白",18,["前端","15k"],21]
// true:开启深拷贝,目标对象:[],原对象:arr
let cloneArr = $.extend(true, [], arr)
arr[0] = "李大白";
arr[2][0] = "java";
console.log(arr, cloneArr);

$extend()第一个参数为true时可以实现深拷贝!但是第一个参数为false,则只会拷贝第一层属性,不能实现深拷贝:

let arr = ["李小白",18,["前端","15k"],21]
// false:不开启深拷贝,目标对象:[],原对象:arr
let cloneArr = $.extend(false, [], arr)
arr[0] = "李大白";
arr[2][0] = "java";
console.log(arr, cloneArr);

总结/注意

实现深拷贝方式有多种,以上三种较为常用,另外还有一个:函数库lodash中_.cloneDeep方法(本人未使用过)也可以实现深拷贝。

最后需要补充一下,网上有很多人说sliceconcat方法可以实现,但我要说的是sliceconcat方法不能实现深拷贝的!!!

下面举例说明:

// slice()
let arr = [0,1,["hello","world"]];
let cloneArr = arr.slice();
arr[0] = "零";
arr[2][0] = "hello2022"
console.log(arr,cloneArr);
// concat()
let arr = [0,1,["hello","world"]];
let cloneArr = arr.concat();
arr[0] = "零";
arr[2][0] = "hello2022"
console.log(arr,cloneArr);

这两个方法得到的结果都是一样的:

这一点大家一定要注意,深拷贝必须要拷贝所有层级的属性,而这两个方法拷贝不彻底,也就是只能拷贝第一层,希望大家不要踩坑!

在js中如何区分深拷贝与浅拷贝?的更多相关文章

  1. js 中引用类型 的深拷贝 和 浅拷贝的区别

    一.曾经在读JQ源码的时候,对深拷贝算是有了一点的理解.我们在项目中是不是经常会遇到这样的问题呢? 后台返回一个数组对象(引用类型).次数在页面渲染中需要对部分数据进行处理 比如:银行卡6234509 ...

  2. 探究JS中对象的深拷贝和浅拷贝

    深拷贝和浅拷贝的区别 在讲深拷贝和浅拷贝的区别之前,回想一下我们平时拷贝一个对象时是怎么操作的?是不是像这样? var testObj1 = {a: 1, b:2}, testObj2=testObj ...

  3. js中的数据类型、以及浅拷贝和深拷贝

    一.js中的数据类型 1.基本类型(值类型):Undefined.Boolean.String.Number.Symbol 2.引用类型:函数.数组.对象.null.new Number(10)都是对 ...

  4. Python中复制、深拷贝和浅拷贝的区别

    深拷贝定义(deepcopy) 在Python中,由于一切皆对象,所以任何变量都可以被引用,也即可以被赋值给任何变量.但是在Python中,给变量赋值,是区分的,一般情况下,Python中的变量赋值都 ...

  5. js引用类型赋值,深拷贝与浅拷贝

    JS中引用类型使用等号“=” 赋值,相当于把原来对象的地址拷贝一份给新的对象,这样原来旧的对象与新的对象就指向同一个地址,改变其中一个对象就会影响另外那个对象,也就是所谓的浅拷贝.例如: var ar ...

  6. PHP中对象的深拷贝与浅拷贝

    先说一下深拷贝和浅拷贝通俗理解 深拷贝:赋值时值完全复制,完全的copy,对其中一个作出改变,不会影响另一个 浅拷贝:赋值时,引用赋值,相当于取了一个别名.对其中一个修改,会影响另一个 PHP中, = ...

  7. 谈谈java中对象的深拷贝与浅拷贝

    知识点:java中关于Object.clone方法,对象的深拷贝与浅拷贝 引言: 在一些场景中,我们需要获取到一个对象的拷贝,这时候就可以用java中的Object.clone方法进行对象的复制,得到 ...

  8. Python中什么是深拷贝和浅拷贝且有什么区别

    浅拷贝: >>> a = [1, 2, 3] >>> b = a >>> a [1, 2, 3] >>> b [1, 2, 3] ...

  9. python中赋值,深拷贝,浅拷贝区别

    这三种 的区别就是 复制的变量 是否是原变量的引用. 赋值:只是原变量的引用. 浅拷贝和深拷贝的区别 需要通过 子元素 区分 浅拷贝:子元素的 引用相同 深拷贝:所以引用都不相同,完全复制一份 这三种 ...

随机推荐

  1. 【LeetCode】676. Implement Magic Dictionary 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 汉明间距 日期 题目地址:https://le ...

  2. C# 基础(更新中)

    Data Structure There're two types of variables in C#, reference type and value type. Enum: enum Colo ...

  3. Log4j 2.17.0 再曝漏洞,但不要惊慌!

    最新消息!根据Log4j官网发布,2.17.0版本还存在漏洞! 上图来自Log4j2官网:https://logging.apache.org/log4j/2.x/ 漏洞编号:CVE-2021-448 ...

  4. 理解Android的四种启动模式

    一:前言 四种模式分别为standard, singleTop, singleTask, singleInstance.自己应该明确一个概念先,single到底要single什么.每一个应用app都有 ...

  5. Handing Incomplete Heterogeneous Data using VAEs

    目录 概 主要内容 ELBO 网络结构 不同的数据 HI-VAE 代码 Nazabal A., Olmos P., Ghahramani Z. and Valera I. Handing incomp ...

  6. Deep Residual Learning for Image Recognition (ResNet)

    目录 主要内容 代码 He K, Zhang X, Ren S, et al. Deep Residual Learning for Image Recognition[C]. computer vi ...

  7. Jmeter环境变量配置你不得不知道的事情

    在安装Jmeter的过程中大家肯定需要配置环境,但是为什么要配置JDK的环境变量呢?大家有没有好奇过,有没有仔细去像一下呢,其实在安装Jmeter前,大家应该都知道Jmeter是我们JAVA开发的,J ...

  8. Capstone通用 USB Type-C音视频拓展坞转换芯片

    专业解决视频接口技术Capstone科技在2021年新推出四款低功耗单芯片USB Type-C音视频格式转换器方案──CS5266.CS5267.CS5268与CS5269.将为各种显示屏.外部显示设 ...

  9. javaScript系列 [27]- DOM

    本文将详细介绍DOM相关的知识点,包括但不限于Document文档结构.Node节点.Node节点的类型.Node节点的关系以及DOM的基本操作( 节点的获取.节点的创建.节点的插入.节点的克隆和删除 ...

  10. Python爬取中国知网文献、参考文献、引证文献

    前两天老师派了个活,让下载知网上根据高级搜索得到的来源文献的参考文献及引证文献数据,网上找了一些相关博客,感觉都不太合适,因此特此记录,希望对需要的人有帮助. 切入正题,先说这次需求,高级搜索,根据中 ...