浅拷贝和深拷贝理解

前端【JS】,深拷贝与浅拷贝的区别及详解!

什么是基本类型值和引用类型值?

基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的 对象

var name = "John"; // 基本类型值
var obj = new Object();  // obj 为引用类型值
obj.name = "John"; 

复制变量

在复制变量中,对于基本类型值来说,两者是互不影响的

var num = 1;
var num1 = num; // num1 = 1;
var num1 = 3; // num还是1,不会变

对于引用类型值来说,复制分为两种,一种是浅拷贝,一种是深拷贝。

一、浅拷贝和深拷贝的区别

对于浅拷贝来说,比如一个数组(数组是一种对象),只要我们修改了一个拷贝数组,那么原数组也会改变

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

因为他们引用的是同一个地址的数据!拷贝的时候并没有给b数组创造独立的内存,只是把a数组指向数据的 指针 拷贝给了b!
而深拷贝就与其相反,将会给b数组创造独立的内存,并且将a数组的内容一一拷贝进来,两者互不影响。

二、浅拷贝和深拷贝的原理(参考了大佬的图~~)

浅拷贝和深拷贝一般是对于引用类型值(如对象)来讲的,而基本类型值(如undefined、null、number、string、boolean以及es6新增的Symbol),只要是复制,就一定是另开辟以存储空间(两者互不影响,修改谁都没问题!)!

①基本数据类型存储:名和值都存在栈内存中(两者互不影响,修改谁都没问题!)


   如let a = 1;

在这里插入图片描述


   当let b = a 时,b复制了a,栈内存会新开辟一个内存

在这里插入图片描述


   所以两者互不影响,修改谁都没问题!

②引用数据类型:名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值(改了b,a也会受到影响。)

在这里插入图片描述


	b复制了a后,b是引用了a的值的存储地址,而不是把a的值复制了下来!

在这里插入图片描述
在这里插入图片描述


  所以改了b,a也会受到影响。

三、实现深拷贝

单层数组深拷贝(此时的brr跟原来的arr是一模一样的,改变了arr后,brr也是没有发生改变的,因为brr的数据空间是一个新的地址。)

1、Array系统构造函数原型上的concat方法
在这里插入图片描述
2、Array系统构造函数运行上的slice方法
在这里插入图片描述


	多层级数组拷贝 如,var a = [0,1,2,[3,4],5];
	a有两层,第一层是[3,4]外围的一层;第二层是[3,4];
 

注意:如果数组中的数据有引用类型数据,上面两个方法对于数组的拷贝,会将这个引用类型数据的地址也拷贝出来
数组中有引用类型数据的情况下,数组的concat()方法和 slice() 方法,不能实现深拷贝
在这里插入图片描述
在这里插入图片描述

**这就是浅拷贝,如果数据中都是基本类型数据,就是完全没有联系的两个数据,但是如果数据中引用类型数据,那两个变量还是有一定的联系。
所谓浅拷贝,也就是说,数组或对象中的值如果是基本类型数据,那拷贝后的数据和原数据是完全没有关联,且互不影响的两个数据,如果数组或对象的值是引用类型数据的话,拷贝后的数组或对象中的引用类型的值跟原数据中的引用类型的值,还是存在共享同一地址的现象。
**

所谓深拷贝,就是不管原数据中值是什么类型的数据,拷贝后的新数据跟原数据是完全没有关联的。

方法1、多层级数组拷贝,用递归实现。
利用递归遍历对象或数组
function deepClone(obj){
		let objClone = Array.isArray(obj)?[]:{};
		for(let i in obj){
			objClone[i] = obj[i];
		}
		return objClone;
	}
	var a = [0,1,2,[3,4],5];
	var b = deepClone(a);

	我们得到的b它的首层是另辟存放空间的,也就是上面所说的[3,4]的外面一层存放在另开辟的存储空间中。
	但是[3,4]这第二层他仍然是引用数据地址,
	因为上面的遍历只遍历了首层!

所以我们应该在上面的代码中加入判断,当碰到数组、对象等时递归函数。

function deepClone(obj){
	//定义对象来判断当前的参数是数组还是对象
	let objClone = Array.isArray(obj)?[]:{};
	//如果obj存在并且为对象		
	if(obj&&typeof obj == "object"){
		for(let key in obj){
			if(obj.hasOwnProperty(key)){
				//如果obj的子元素为对象,那么递归(层级遍历)
				if(obj[key]&&typeof obj[key] == "object"){
					objClone[key] = deepClone(obj[key]);
				}else{
				//如果不是,直接赋值
					objClone[key] = obj[key];
				}
			}
		}
	}	
	return objClone;
}
方法2、JSON解析(底层原理也是层级遍历)

利用json数据和json字符串之间的转换


  依旧是上面的例子,
  var a =  [0,1,2,[3,4],5];
  var b = JSON.parse(JSON.stringify(a));
  

在这里插入图片描述
在这里插入图片描述


	b就是拷贝的结果,修改b不影响a。但是这种方法也有缺陷:

	1- 无法复制函数
	2- 原型链没了,对象就是object,所属的类没了。

总之,赋值是赋值,拷贝是拷贝。

浅拷贝,当对象或数组中的数据都是基本数据类型的时候,两个数据之间完全是独立的,如果对象或数组中的值是引用类型的时候,里面是引用类型的值,还是会保持共同的内存地址;

深拷贝出来的两个数据是完全独立的。




补充知识点: JSON.stringify()和JSON.parse()

一、 JSON.stringify() 对 JavaScript 对象进行字符串化

JSON 的常规用途是同 web 服务器进行数据交换。
在向 web 服务器发送数据时,数据必须是字符串。
通过 JSON.stringify() 把 JavaScript 对象或数组对象 转换为字符串。

var obj = { name:"Bill Gates", age:62, city:"Seattle"};
var myJSON = JSON.stringify(obj);


var arr = [ "Bill Gates", "Steve Jobs", "Elon Musk" ];
var myJSON = JSON.stringify(arr);

结果将是遵守 JSON 标记法的一段字符串。
myJSON 目前是一段字符串,并准备好送往服务器:


二、 JSON.parse() 将数据转换为 JavaScript 对象。

JSON 通常用于与服务端交换数据。
在接收服务器数据时一般是字符串。
我们可以使用 JSON.parse() 方法将数据转换为 JavaScript 对象。

例如我们从服务器接收了以下数据:

{ "name":"runoob", "alexa":10000, "site":"www.runoob.com" }

我们使用 JSON.parse() 方法处理以上数据,将其转换为 JavaScript 对象:

var obj = JSON.parse('{ "name":"runoob", "alexa":10000, "site":"www.runoob.com" }');

解析数据
JSON 不能存储 Date 对象。

如果你需要存储 Date 对象,需要将其转换为字符串。

之后再将字符串转换为 Date 对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值