JS浅拷贝和深拷贝

今天有人问到了JS中的浅拷贝和深拷贝,今天索性来写写,那里写的不好还望大佬可以批评指正,好了好不多说步入正题。

深入了解——数据在JS中的储存机制(不想了解的直接看下面的深拷贝和浅拷贝)
1.了解存储机制
  1. 在JS中对于数据的储存分为两大类 基本数据类型(简单数据类型)引用数据类型(复杂数据类型)
  2. 基本数据类型 例如: Number String undefined null Boolean Symbol BigInt
  3. 引用数据类型 例如:Function Array Object Date Math
  4. 存储数据的容器分为 栈内存堆内存
  5. 栈内存:只允许在一段进行插入或删除的线性表,是一种先进后出的数据结构(了解就好);
  6. 堆内存:基于散列算法的数据结构(了解就好);
  7. 基本数据类型是直接在栈内存中占据一段空间直接存储的(把变量名和值【内容】直接存储在栈内存);
  8. 引用数据类型 是把值(内容)存储在堆内存中,然后在栈内存中占据一段空间存储变量名和在堆内存中内容的地址;
2.代码示例(如果不理解代码,请看完这些,再看下图示就理解了)

基本数据类型:

	var a=1;
	var b=a;
	a=2;
	console.log(b); // 1

因为基本数据类型在栈内存中是线性储存,变量名直接对应这个值如(a:1),当把a赋值给b,则等于把1赋值给b,在栈内存中存储为( b:1);

引用数据类型

	var obj1={
	    name:"小破船借箭",
	    age:18
	};
	var obj2=obj1;
	obj1.age=24;
	console.log(obj1);//{name:"小破船借箭",age:24}
	console.log(obj2);//{name:"小破船借箭",age:24}

因为引用数据类型会把内容存储在堆内存中,而把内容对应的堆内存的地址和变量名存储在栈内存中如 (obj1:obj1内容地址),当把obj1赋值给obj2时实际是把obj1的地址赋值给了obj2 如(obj2:obj1内容地址),所以在更改obj1中的属性时,打印obj2得到的值与obj1的值相同。

3.画图释义+解析

基本数据类型
在这里插入图片描述

因为基本数据类型在栈内存中是线性储存,变量名直接对应这个值如(a:1),当把a赋值给b,则等于把a对应的值也就是1赋值给b,所以在栈内存中存储为( b:1);

引用数据类型
在这里插入图片描述

因为引用数据类型会把内容存储在堆内存中,而把内容对应的堆内存的地址和变量名存储在栈内存中如 (obj1:obj1内容地址),当把obj1赋值给obj2时实际是把obj1对应的值也就是obj1的内容地址赋值给了obj2 如(obj2:obj1内容地址),这时它们指向相同,属性和方法都可以公用 ,所以在这里更改obj1中的属性时,obj2拿到的属性也是更改后的。

4.JS中为什这么储存
  • 基本数据类型相对比较稳定,而且对内存的占用也比较少,所以栈内存的处理速度相对来说要比堆内存要快;
  • 引用数据类型内容的大小是动态并且无限的,直接根据引用获取,存储方式无序;
浅拷贝
什么是浅拷贝?

浅拷贝简单来说 拷贝的如果是基本数据类型就是直接拷贝值,如果是引用数据类型就是拷贝的引用地址。(不理解这句的请看上面的JS存储机制)

代码示例一 (经典案例)
	var obj1={
	    name:"小破船借箭",
	    age:18
	};
	var obj2=obj1;
	console.log(obj2);//{name:"小破船借箭",age:18} 没修改值之前打印的是age是18
	obj1.age=24;
	console.log(obj1);//{name:"小破船借箭",age:24} obj1被修改后的结果
	console.log(obj2);//{name:"小破船借箭",age:24} 修改值之后打印的是age是24 与 obj1相同

弱弱的说句,这里不懂的请上移看下JS存储机制(谢谢)

代码示例二 (经典案例扩展)
     var obj1 = {
          age: 18,
          name: "小破船借箭",
          specialty:{
              name:'游泳',
              beginAge:10
          },
          like:['旅游','看妞']
      };
      var obj2 = {};
      //写的一个拷贝方法
      function shallowCopy(obj,targetObj){
          for (let key in obj){
              targetObj[key] = obj[key];
          }
      }
      //把obj1拷贝给obj2
      shallowCopy(obj1, obj2);

      console.log(obj2);
      //没修改之前的值{ age: 18,name: "小破船借箭",specialty:{name:'游泳', beginAge:10},like:['旅游','看妞']}
      obj1.like.push("睡觉");
      console.dir(obj1);
      //修改之后obj1的值{ age: 18,name: "小破船借箭",specialty:{name:'游泳', beginAge:10},like:['旅游','看妞','睡觉']}
      console.log(obj2);
      //修改之后obj2的值{ age: 18,name: "小破船借箭",specialty:{name:'游泳', beginAge:10},like:['旅游','看妞','睡觉']}与obj1的值相同
      obj1.age=24; 
      //再次更改obj1的属性
      console.dir(obj1);
      //修改之后obj1的值{ age: 24,name: "小破船借箭",specialty:{name:'游泳', beginAge:10},like:['旅游','看妞','睡觉']}
      console.log(obj2);
      //修改之后obj2的值{ age: 18,name: "小破船借箭",specialty:{name:'游泳', beginAge:10},like:['旅游','看妞','睡觉']}与obj1的值不相同        

代码解析:这段代码中,有人会发现第二次更改obj1的属性值,obj2的属性值没有发生变化。那是因为在这段代码中,我们使用到了for···in循环,那么我们对属性一对一赋值时那么name属性和age属性是不是就是基本数据类型,基本数据类型是在栈内存中直接储存对应的值,所以specialty属性和like属性就是引用数据类型,引用数据类型是在堆内存中储存对应的引用地址,所以第二次更改,obj2不受影响。

深拷贝
什么是深拷贝?

深拷贝就是把一个对象(引用数据类型)里面包含的属性和方法都一个个找出来(包括多层嵌套的属性何方法),然后在另一个对象里面一个个对应的在堆内存中储存起来;

代码示例一 (脑残方法)
	var obj1={
	    name:"小破船借箭",
	    age:18
	};
	var obj2={
	    name:obj1.name,
	    age:obj1.age
	};
	obj1.age=24;
	console.log(obj1);//{name:"小破船借箭",age:24} age被修改后
	console.log(obj2);//{name:"小破船借箭",age:18} 与obj1不相同,obj1更改obj2不被影响
代码示例二 (把代码转JSON字符串)
	var obj1={
	    name:"小破船借箭",
	    age:18
	};
	var obj2=JSON.parse(JSON.stringify(obj1));
	obj1.age=24;
	console.log(obj1);
	console.log(obj2);

这个方法确实好用,不过是存在一些缺点的:

  • 比如undefinedFunctionRegExp等是没有不能被转JSON的
  • 使用JSON进行深拷贝后,这个对象原来的构造函数(constructor),会变成Object;
代码示例三 (递归拷贝)
	var obj1 = {
	    age: 18,
	    name: "小破船借箭",
	    specialty: {
	        name: '游泳',
	        beginAge: 10
	    },
	    like: ['旅游', '看妞']
	};
	var obj2 = {};
	function deepClone(initObj, targetObj) {
	    for (let key in initObj) {
	        let item = initObj[key];
	        if (item instanceof Array) {
	            targetObj[key] = [];
	            deepClone(item, targetObj[key]);
	        } else if (item instanceof Object) {
	            targetObj[key] = {};
	            deepClone(item, targetObj[key]);
	        } else {
	            targetObj[key] = initObj[key];
	        }
	    }
	}
	deepClone(obj1, obj2);
	obj1.specialty.name="爬山";
	console.log(obj1);
	//{ age: 18,name:"小破船借箭",specialty:{name:'爬山', beginAge:10},like:['旅游','看妞']} 修改后obj1的值发生变化 
	console.log(obj2);
	//{ age: 18,name:"小破船借箭",specialty:{name:'游泳', beginAge:10},like:['旅游','看妞']} 修改后obj2的值与obj1的值不相同

代码解析:在浅拷贝中我们使用了for···in循环复制了obj1对象的第一层,在这里我们是使用for···in最递归循环,判断当前对象的key是什么类型的,如果是Object或者Array,我们就把当前遍历到的key的属性值再传给该函数,依次循环,知道找到最里层为止。这样我们就等于为这个新的对象开辟了新的空间进行储存了。

后续再更新别的方法!!!

.
.
.
谢谢您的阅读很高兴,如果有什么不对的地方还望批评指正。
既然看到了这里,如果有帮助的话,希望给小破船点个赞吧。谢谢

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值