首页 > 其他分享 >js--深拷贝和浅拷贝

js--深拷贝和浅拷贝

时间:2023-10-26 10:55:22浏览次数:34  
标签:obj log -- 数据类型 js let console 拷贝

一、栈(stack)和堆(heap)

  • 栈(stack):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;每种数据类型占用的空间大小是固定的。
  • 堆(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表;

二、基本数据类型和引用数据类型

  • 基本数据类型:Number、String、Boolean、Null、 Undefined、Symbol(ES6)。
  • 引用数据类型:Object、Array、Function、Date、RegExp、Map、Set等。

  基本数据类型和引用数据类型的存储,是不同的。

  内存地址分配:

     1)基本数据类型:将值存储在栈中 ,栈中存放的是对应的值
     2)引用数据类型:将对应的值存储在堆中,栈中存放的是指向堆内存的地址

 

  赋值变量:

     1)基本数据类型:是生成相同的值,两个对象对应不同的地址
     2)引用数据类型:是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆内存中同一个对象

	let a = 10;
	let b = a;  // 赋值操作
	b = 20;
	console.log(a);  // 10

  总结:a是基本类型,存储在栈中;把a赋值给b,虽然两个变量的值相等,但是两个变量保存了两个不同的内存地址。

	let obj1 = {}
	let obj2 = obj
	obj2.name = '李四'
	console.log(obj.name)  // 李四

  解释:obj1是引用类型,将数据存放在堆内存中,而栈中存放的是内存地址.在obj1赋值给obj2,实际是将obj1的引用地址复制了一份给了obj2,实际上他们共同指向了同一个堆内存对象,所以更改obj2会对obj1产生影响。这就是所谓的浅拷贝。

  总结:为什么拷贝完了,值会随它的变化而变化? 因为拷贝的是它的指针,实际的值,指向同一个。

所以,如果a, b两个变量,b赋值给a , 当b修改时,a是否变化?如果a变化了,就是浅拷贝,a没变化,就是深拷贝。

浅拷贝:

会在栈中开辟另一块空间,并将被拷贝对象的栈内存数据完全拷贝到该块空间中,即基本数据类型的值会被完全拷贝,而引用类型的值则是拷贝了“指向堆内存的地址”。

常见的浅拷贝方法:

  • Object.assign()
  • 扩展运算符(…)
  • Array.concat()
  • Array.slice()

举例:

var obj = { 
	x: 2, 
	y: 3,
	z: { 
		num: 9 
	} 
}
var newObj = {}
Object.assign(newObj, obj)
newObj.y = 6
console.log(obj)  
console.log(newObj) 

注意:

  • Object.assign()不会拷贝对象的继承属性;
  • Object.assign()不会拷贝对象的不可枚举的属性;
  • Object.assign()可以拷贝 Symbol 类型的属性。

 对象的拷贝:

const obj = {
 a: 1,
 b: {
   c: 1
 }
}
const obj2 = {...obj}
obj.a = 2

console.log(obj)
console.log(obj2);  

数组的拷贝:

let arr = [1, 2, 3];
let newArr = [...arr]; // 跟arr.slice()是一样的效果

注意:扩展运算符 和 object.assign 有同样的缺陷,也就是实现的浅拷贝的功能差不多,但是如果属性都是基本类型的值,使用扩展运算符进行浅拷贝会更加方便。  

var obj1 = ["Chinese", { "name": "zs" }, "French"]
var obj2 = obj1.concat()
obj2[1].name = "ls"
obj2[2] = "China"
console.log(obj1, obj2);

注意:数组的 concat 方法其实也是浅拷贝,所以连接一个含有引用类型的数组时,需要注意修改原数组中的元素的属性,因为它会影响拷贝之后连接的数组。不过 concat 只能用于数组的浅拷贝,使用场景比较局限。 

var arr = [2, 4, 6, { y: 10 }]
var newArr = arr.slice()  //slice 的语法为:arr.slice(begin, end);
newArr[0] = 10
newArr[3].x = 20
newArr[3].y = 30
console.log(arr)
console.log(newArr)

注意:slice 方法也比较有局限性,因为它仅仅针对数组类型。slice 方法会返回一个新的数组对象,这一对象由该方法的前两个参数来决定原数组截取的开始和结束位置,是不会影响和改变原始数组的。但是,数组元素是引用类型的话,也会影响到原始数组。

深拷贝:  

深拷贝是拷贝多层,每一级别的数据都会拷贝出来。

常见的深拷贝方法:

  • JSON.parse(JSON.stringify(obj))
  • 递归方法
  • 函数库 lodash(_.cloneDeep)

举例:

let a = {name: '张三', age: 19, like: ['打篮球', '唱歌', '跳舞']}
let b = JSON.parse(JSON.stringify(a)) //原理:用 JSON.stringify 将对象转成 JSON 字符串,再用 JSON.parse()把字符串解析成对象。一去一来,新的对象就产生了,而且对象会开辟新的栈,实现深拷贝。
a.name = '李四'
a.like[0] = '睡觉'
console.log(a)  
console.log(b)  

注意:JSON.stringify也有局限性。

  1. 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
  2. 拷贝 Date 引用类型会变成字符串;
  3. 无法拷贝不可枚举的属性;
  4. 无法拷贝对象的原型链;
  5. 拷贝 RegExp 引用类型会变成空对象;
  6. 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
  7. 无法拷贝对象的循环应用,即对象成环 (obj[key] = obj)。

递归:

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

lodash函数库提供 _.cloneDeep用来做深拷贝:

	let _ = require('lodash');
	let obj = {
	    a:1,
	    b:{f:{g:1}},
	    c:[1,2,3]
	};
	let newObj = _cloneDeep(obj);
	console.log(obj.b.f === newObj.b.f); //true

 总结:

  • 浅拷贝就是只拷贝基础数据类型的数据,并且数据只有单一的一层,而深拷贝用于拷贝具有复杂的数据类型的数据

应用场景:

  • 浅拷贝主要用于你需要拷贝的对象的数据结构只有基础数据类型。
  • 深拷贝主要用于你想操作该数据,但是又不想影响到原数据的时候,就可以进行深拷贝。

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

标签:obj,log,--,数据类型,js,let,console,拷贝
From: https://www.cnblogs.com/Super-scarlett/p/17787803.html

相关文章

  • 中小型企业选择CRM系统时应该注意哪些?
      如今市面上充斥着各种各样的CRM客户管理系统,尽管功能说的天花乱坠,中小企业选型时还是应该以自身需求为主。下面是中小企业选型CRM系统的几个要点,大家可以根据以下需求来筛选。1、明确自身需求决定企业选择哪一个CRM系统的前提应是需求。要明确需要使用CRM系统满足当下的......
  • Spring Boot版本号介绍和使用说明
    官网地址:https://spring.io/projects/spring-boot#learn下面是从官网的截图的官方版本号SpringBoot的版本号分析:1)主版本,主版本一般情况下是不变的,除非有大机制或者是架构的调整,才会去调整主版本,且主版本之间一般是不相兼容的。2)次版本,次版本主要是在主版本架构不管的......
  • 捡起ctf学习 day2 Linux BUU LFI COURSE 1(Local File Include) burpsuite爆破
    1.LocalFileInclude打开网页 文件包含漏洞,参考知乎专栏https://zhuanlan.zhihu.com/p/540864302随着网站的业务的需求,程序开发人员一般希望代码更加灵活,所以将被包含的文件设置为变量,用来进行动态调用,但是正是这种灵活性通过动态变量的方式引入需要包含的文件时,用户对这个......
  • 【论文阅读笔记】【OCR-文本识别】 Towards Accurate Scene Text Recognition with Se
    SRNCVPR2020读论文思考的问题论文试图解决什么问题?如何利用文本的上下文语义信息来辅助文本识别任务RNN能部分利用语义信息,但它的利用方式是串行的,极大地限制了语义信息的帮助,会造成错误累积以及效率缓慢等问题文章提出了什么样的解决方法?提出全局语义理解......
  • CF888C题解
    分析容易想到可以枚举每个字母,分别求其最小\(k\)取\(\min\)。思考对于一个\(k\),如何判其不合法。容易想到如果存在一个没有这个字符的长度大于等于\(k\)的子段,那么这个\(k\)就不合法。那么我们就知道如何求最小合法\(k\)了。找到最长的没有这个字符的子段,其长度加......
  • 【论文阅读笔记】【OCR-文本识别】 Read Like Humans: Autonomous, Bidirectional and
    ABINetCVPR2021(Oral)读论文思考的问题论文试图解决什么问题?如何对语言的上下文进行建模而不是对视觉特征的上下文信息进行建模如何在端到端的文本识别模型中更好、更高效地对文本的语言知识进行建模,提升对困难情况的字符识别效果文章提出了什么样的解决方法?......
  • 【Python】venv、virtualenv _ 虚拟环境库
    虚拟环境:从电脑独立开辟出来的python环境,可以把它看作一个容器,我们可以在这个容器(环境)中安装我们项目中所依赖的相关模块和包。 虚拟环境的优点1.不同的虚拟环境相互独立,不会影响到其他应用。2.防止出现包管理混乱和版本冲突。3.不会影响全局的python环境。   ......
  • 【论文阅读笔记】【OCR-文本识别】 From Two to One: A New Scene Text Recognizer wi
    VisionLANICCV2021读论文思考的问题论文试图解决什么问题?使用语言模型对识别的文本的上下文语义信息进行建模时,会有以下问题:引入额外的计算量;识别的视觉和语言特征很难做一个很好的融合、互补能否在不使用语言模型的情况下,直接赋予视觉模型一定的语言建模能力?......
  • CSP-S 2023 游寄
    怎么,会有人,连挂四年!111Day0请假!喜提双休,想想就开心!111晚上在家享受动画片,非常爽!Day1起床了,欸有卧槽,12点了(#°Д°),匆匆忙忙上路。13:10抵达河南省某211高校,赢!13:30进场,左右两边都是小朋友,还问我怎么解压文件(lll¬ω¬)。14:30启动!A闹碳题,瞬间写完!我靠怎么15......
  • VSCode下载安装
    下载安装1.双击安装程序,勾选同意协议,点击下一步。 2.选安装地址   3.继续下一步 4.创建桌面快捷方式 5.安装           使用VSCode的教程参考链接:史上最全vscode配置使用教程-知乎(zhihu.com) ......