LastPoem / Restart

2 stars 0 forks source link

内存空间 #67

Open LastPoem opened 4 years ago

LastPoem commented 4 years ago

在JavaScript中具有自动垃圾回收机制,因此前端开发中内存空间很少提及。但是对它的模糊认知会导致对很多知识的理解不到位,比如引用数据类型和引用传递,浅拷贝和深拷贝,闭包等等。

想要对JS有更深的理解,就必须对内存空间有个清晰的认知。在学习内存空间前,需要先理解三种数据结构,分别是堆heap,栈stack和队列queue。

一、栈数据结构

与C/C++不同,JavaScript中没有严格意义上区分栈内存和堆内存。因此大部分情况下可以简单粗暴的认为JavaScript所有数据都保存在堆内存中。但某些场景中,仍然需要基于栈数据结构的思维实现一些功能,比如JavaScript中的执行上下文。执行上下文的执行顺序借用了栈数据结构的存取方式(函数调用栈)。因此理解栈数据结构的原理与特点十分重要。

栈类似于一个只有一个口的盒子,数据只能先进后出,后进先出。

二、堆数据结构

堆数据结构是一种树状结构,它存取数据的方式和书架和书非常类似。 书放在书架上,只要知道书的名字,就可以很方便的取出,而不用像栈那样要把上面的数据取出才能拿到中间的数据。

三、队列

理解队列数据结构的目的主要是为了清洗的明白事件循环机制。

队列是一种先进先出的数据结构,就像排队过安检一样,排在最前面的人最先过去。

四、变量对象与基础数据类型

JavaScript的执行上下文生成后,会创建一个叫变量对象的特殊对象,JavaScript基础数据类型往往会保存在变量对象中。

严格意义上说,变量对象也是存放在堆内存中,但是由于变量对象的特殊职能,在理解时仍需将其与堆内存区分开。

基础数据类型都是一些简单的数据段,JavaScript中有五种基本数据类型,分别是Number,String,Boolean,Undefined,Null.基础数据类型都是按值访问,可以直接操作保存在变量中的实际值。 ES6中新增了一种基础数据类型Symbol,可以先不考虑。

五、引用数据类型与堆内存

与其他语言不同,JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JavaScript不允许直接访问堆内存中的数据,因此不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值是按引用访问的。这里的引用,可以理解为保存变量对象中的一个地址,该地址与堆内存的实际值相关联。

let a1 = 0 // 变量对象 let a2 = 'this is string' // 变量对象 let a3 = null // 变量对象

let b = { m: 20 } // 变量b存在于变量对象中,{ m: 20 }作为对象存在于堆内存中 let c = [1, 2, 3] // 变量c存在于变量对象中,[1, 2, 3]作为对象存在于堆内存中

当要访问堆内存中的引用数据类型时,实际上首先是从变量对象中获取了该对象的地址引用(或地址指针,然后再从堆内存中取得需要的数据。)

也就是说,对于基础数据类型,变量对象存的是具体值,对于引用数据类型,变量对象存的是引用数据的地址。

题目: var a = 20; var b = a; b = 30 此时a的值是多少? 答案是20

var m = { a: 10, b: 20 } var n = m; n.a = 15 此时m.a的值是多少?答案是15,因为m和n访问的是同一个地址指针。 通过var n = m执行一次复制引用类型的操作,引用类型的复制同样也会为新的变量自动分配一个新的值保存在变量对象中。但不同的是,这个新的值,仅仅只是引用类型的一个地址指针。当地址指针相同时,在变量对象中访问到的具体对象实际上是同一个。因此改变n时,实际上修改的是m和n共同使用的具体对象,m也就发生了改变。

六、内存空间管理

JavaScript具有自动垃圾收集机制,所以一般不需要关心内存使用问题。

JavaScript内存生命周期 1.分配所需要的内存 2.使用分配到的内存(读,写) 3.不需要时将其释放,归还

例如: var a = 20 //在内存中给数值变量分配空间 alert( a + 100 ) //使用内存 a = null //使用完毕后,释放内存空间

JavaScript自动垃圾收集机制就是找出那些不再继续使用的值,然后释放其占用的内存。垃圾收集器会每隔固定的时间段就执行一次释放操作。 最常用的是通过标记清除的算法找到哪些对象是不再继续使用的,因此a = null 实际上只是做了一个释放引用的操作,让a原本对应的值失去引用,脱离执行环境,这个值会在下一次垃圾收集器执行操作时被找到并释放。在适当的时候解除引用,是为页面获得更好性能的一个重要方式。

在局部作用域中,当函数执行完毕,局部变量就没有存在的必要了。因此垃圾收集器很容易做出判断并回收。但是全局变量什么时候需要释放内存空间则很难判断,因此在开发中原则上应避免使用全局变量(尽量少使用全局变量)。