1.JS数据类型

数据类型

基础数据

1. 基础数据类型在内存中,以键值对的形式存储在栈内存中;

在这里插入图片描述

引用类型

1. 引用数据类型的存储,如下图所示,栈内存中存放的是变量名及对应值的内存引用地址;  

在这里插入图片描述

浅拷贝/深拷贝

浅拷贝: 指的是直接将a变量赋值给b变量,当a为基本数据类型时,a,b的值相互独立,即基本数据类型的拷贝;当a变量为引用类型时,浅拷贝获得的b变量对应的内存引用地址与a相同,因此修改任一变量都会影响另一个变量;

深拷贝: 特指引用类型数据的拷贝赋值,对应引用类型数据的拷贝,即引用类型变量a赋值给变量b,实现的是值的完全拷贝,深拷贝后的引用变量a和引用变量b,相互独立;

深拷贝方法:

1. JSON类型强制转换  
	var a = {name:'ming',age:18};
	var b = JSON.parse(JSON.stringify(a))
2. lodash库方法常用拷贝
  import _ from 'lodash';
  	var a = {name:'ming',age:18};
	// 浅拷贝,只针对指针的拷贝,两个变量指针指向同一个内存地址,互相影响
	var b  = _.clone(a);
	// 深拷贝,内存地址(指针)及值的完全拷贝,两个变量相互独立;
	var b = _.cloneDeep(a);
	
	/*浅对比拷贝*/
	//_.defaults(object, [sources]),object:目标对象;[source]:涞源对象
	// 一旦设置了相同属性的值,后续的将被忽略掉。
	_.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
	// => { 'a': 1, 'b': 2 }  
	
	/*深对比拷贝*/  
	//_.defaultsDeep(object, [sources]),递归分配默认属性,如果目标对象没有该属性,则添加
	_.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });
	// => { 'a': { 'b': 2, 'c': 3 } }

	/*注意*/  
	原生Object.assign(object,[source]) 无法实现深拷贝
	Object.assign==> 合并多个JavaScript对象(第一个参数是目标对象,后面的都是源对象,assign方法将多个原对象的属性和方法都合并到了目标对象上面,如果在这个过程中出现同名的属性(方法),后合并的属性(方法)会覆盖之前的同名属性(方法)
	
3. 递归实现深拷贝
var obj = {   //原数据,包含字符串、对象、函数、数组等不同的类型
       name:"test",
       main:{
           a:1,
           b:2
       },
       fn:function(){
           
       },
        friends:[1,2,3,[22,33]]
   }

   function copy(obj){
        let newobj = null;   //声明一个变量用来储存拷贝之后的内容
        
     //判断数据类型是否是复杂类型,如果是则调用自己,再次循环,如果不是,直接赋值即可,
     //由于null不可以循环但类型又是object,所以这个需要对null进行判断
        if(typeof(obj) == 'object' && obj !== null){ 
	//声明一个变量用以储存拷贝出来的值,根据参数的具体数据类型声明不同的类型来储存
            newobj = obj instanceof Array? [] : {};     
	//循环obj 中的每一项,如果里面还有复杂数据类型,则直接利用递归再次调用copy函数
            for(var i in obj){  
                newobj[i] = copy(obj[i])
            }
        }else{
            newobj = obj
        }    
      return newobj;    //函数必须有返回值,否则结构为undefined
   }

    var obj2 = copy(obj)
    obj2.name = '修改成功'
    obj2.main.a = 100
   console.log(obj,obj2)

特点

  1. 基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中;
  2. 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本;
  3. 引用类型的值是对象,保存在堆内存中;
  4. 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;
  5. 从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象;

垃圾回收

	垃圾回收:只针对引用类型数据的回收,基本数据类型存放在栈内存中,使用之后会自动回收;

	1. 标记清除
		垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。
		然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。
		而在此之后再被加上标记的变量将被视为准备删除的变量,
		原因是环境中的变量已经无法访问到这些变量了。

		理解:在内存中(堆内存)假设所有变量(堆中存储的)都需要清除;
		然后遍历执行环境中所有的变量以及变量引用的变量(执行环境中的变量,在栈内存中),
		然后删除这些“有用”的变量身上的标记(删除执行环境中变量对应的存储变量/值);
		剩余,在堆内存中还有标记的内存变量,此时已经没有执行环境变量(栈中)可以访问到这些变量(堆内存中),因此这些无法被访问的变量就可以被清除了;

	例比:标记有用对象的过程其实就是可达测试的过程。你可以想象整个内存是一个大海,每个对象都是一个岛屿,相互之间用大桥连接,现在要找出与大陆相连的岛屿,那么就从大陆出发,依次标记所能达到的每个岛屿,最后没有被标记到的岛屿就是孤岛,可以当垃圾清除掉。即使两个孤岛之间相互有桥连接也没用,因为与大陆不通啊。这个大陆就是应用程序,或者说是应用程序里的根对象。每次遍历的时候都是从这个根对象出发的。
	参考:  https://segmentfault.com/q/1010000018711986?utm_source=sf-similar-question


	2. 引用次数
		其含义是跟踪记录每个值被引用的次数,如果声明了一个变量a,并将引用类型值赋值给a,则该引用类型值的引用次数+1;反之,如果引用该值的变量a重新取了另一个引用类型值,则该值的引用次数-1;当引用次数为0时,此时垃圾收集器任务该引用类型值‘无用’,即可回收;

		缺点:循环引用。循环引用指的是对象A 中包含一个指向对象B 的指针,而对象B 中也包含一个指向对象A 的引用;
function problem(){
	var objectA = new Object();
	var objectB = new Object();
	objectA.someOtherObject = objectB;
	objectB.anotherObject = objectA;
}

如图,objectA和objectB对应的引用类型值(即0x123和0x888对应的存储值);产生循环引用后,0x123分别被objectA,objectB.anotherObject;0x888分别被objectB,objectA.someOtherObject 引用2次,当执行环境执行完毕后,即objectA和objectB被释放掉,但是无法清除堆内存中的引用,因此两个引用类型的值就不会“清零”,因此标记清除策略更常用;
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值