Loading... > 本文查阅了部分相关资料撰写,如有侵权行为请联系博主修改。 因为JavaScript的自动垃圾回收机制,在前端开发中,内存空间貌似是一个并不常听到的概念。 也许你也对这种计底层的知识一无所知,那或许在以后的某个时间段里也会和我一样发现由于对它的模糊认知,导致了许多知识理解得并不明白。比如最基本的引用数据类型和引用传递到底是怎么回事儿?浅复制与深复制有什么不同?闭包到底是什么?等等。 因此如果你需要精通js,那么熟知内存空间就是必走的路。 ```javascript //一个简单的闭包 function bar(){ //外层函数声明的变量 var value=1; function foo(){ console.log(value); } return foo(); }; var bar2=bar; //实际上bar()函数并没有因为执行完就被垃圾回收机制处理掉 //这就是闭包的作用,调用bar()函数,就会执行里面的foo函数,foo这时就会访问到外层的变量 bar2(); ``` ## 栈/堆/队列数据结构 首先需要基础的储备知识:<span class="external-link"><a href="https://baike.baidu.com/item/%E5%A0%86%E6%A0%88/1682032?fr=aladdin" target="_blank">关于堆栈的百度百科<i data-feather='external-link'></i></a></span> 当你理解了堆栈以及队列是什么以后,接下来就很易于学习了。 ### 栈数据结构 栈数据结构就像乒乓球盒子,但JS与C/C++不同,JavaScript中并没有严格意义上区分栈内存与堆内存。因此我们可以简单粗暴的理解为JavaScript的所有数据都保存在堆内存中。但是在某些场景,我们仍然需要基于栈数据结构的思维来实现一些功能,比如JavaScript的执行上下文。执行上下文的执行顺序借用了栈数据结构的存取方式(也就是后面我们会经常提到的函数调用栈)。因此理解栈数据结构的原理与特点十分重要。 ### 堆数据结构 堆数据结构是一种树状结构。它的存取数据的方式,则与书架与书非常相似。 书虽然也整齐的存放在书架上,但是我们只要知道书的名字,就可以很方便的取出我们想要的书,而不用像从乒乓球盒子里取乒乓一样,非得将上面的所有乒乓球拿出来才能取到中间的某一个乒乓球。好比在JSON格式的数据中,我们存储的 `key-value`是可以无序的,因为顺序的不同并不影响我们的使用,我们只需要关心书的名字。 ### 队列 在JavaScript中,理解队列数据结构的目的主要是为了清晰的明白事件循环(Event Loop)的机制到底是怎么回事。 队列是一种先进先出(FIFO)的数据结构。正如排队过安检一样,排在队伍前面的人一定是最先过检的人。 --- <div class="tip inlineBlock success">那么堆、栈、队列之间的区别是什么呢 **堆**是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。 **栈**就是一个桶(或者说火车站),后放进去的先拿出来,它下面本来有的东西要等它出来之后才能出来。(后进先出) **队列**只能在队头做删除操作,在队尾做插入操作.而栈只能在栈顶做插入和删除操作。(先进先出) </div> --- > 以下部分内容摘录于公众号《图雀社区》 ## 变量对象与基础数据类型 JavaScript的执行上下文生成之后,会创建一个叫做变量对象的特殊对象,JavaScript的基础数据类型往往都会保存在变量对象中。 > 严格意义上来说,变量对象也是存放于堆内存中,但是由于变量对象的特殊职能,我们在理解时仍然需要将其与堆内存区分开来。 基础数据类型都是一些简单的数据段,JavaScript中有5种基础数据类型,分别是 `Undefined、Null、Boolean、Number、String`。基础数据类型都是按值访问,我们可以直接操作保存在变量中的实际值。(ES6中新加了一种基础数据类型Symbol,可以先不用考虑他) ## 引用数据类型与堆内存 与其他语言不同,JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JavaScript不允许直接访问堆内存中的数据,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以理解为保存在变量对象中的一个地址,该地址与堆内存的实际值相关联。 为了更好的搞懂变量对象与堆内存,我们可以结合以下例子与图解进行理解。 ```javascript var a1 = 0; // 变量对象 var a2 = 'this is string'; // 变量对象 var a3 = null; // 变量对象 var b = { m: 20 }; // 变量b存在于变量对象中,{m: 20} 作为对象存在于堆内存中 var c = [1, 2, 3]; // 变量c存在于变量对象中,[1, 2, 3] 作为对象存在于堆内存中 ``` <img src="https://yhevis.top/usr/uploads/2020/07/305074359.png" alt="image.png" /> 当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量对象中获取了该对象的地址引用(或者地址指针),然后再从堆内存中取得我们需要的数据。 理解了JS的内存空间,我们就可以借助内存空间的特性来验证一下引用类型的一些特点。 在前端面试中我们常常会遇到这样一个类似的题目 ```javascript // demo01.js var a = 20; var b = a; b = 30; // 这时a的值是多少? ``` ``` // demo02.js var m = { a: 10, b: 20 } var n = m; n.a = 15; // 这时m.a的值是多少 ``` 在变量对象中的数据发生复制行为时,系统会自动为新的变量分配一个新值。`var b = a`执行之后,a与b虽然值都等于20,但是他们其实已经是相互独立互不影响的值了。具体如图。所以我们修改了b的值以后,a的值并不会发生变化。 <img src="https://yhevis.top/usr/uploads/2020/07/2986514900.png" alt="image.png" /> <img src="https://yhevis.top/usr/uploads/2020/07/2448406933.png" alt="image.png" /> <img src="https://yhevis.top/usr/uploads/2020/07/2750013010.png" alt="image.png" /> 通过内存的角度来理解,是不是感觉要轻松很多?除此之外,我们还可以以此为基础,一步一步的理解JavaScript的执行上下文,作用域链,闭包,原型链等重要概念。 ## 内存空间管理 因为JavaScript具有自动垃圾收集机制,所以我们在开发时好像不用关心内存的使用问题,内存的分配与回收都完全实现了自动管理。但是根据以往的开发经验,了解内存机制有助于自己清晰的认识到自己写的代码在执行过程中发生过什么,从而写出性能更加优秀的代码。 关心内存是一件非常重要的事情。 JavaScript的内存生命周期 1.分配你所需要的内存2.使用分配到的内存(读、写)3.不需要时将其释放、归还 为了便于理解,我们使用一个简单的例子来解释这个周期。 ``` var a = 20; // 在内存中给数值变量分配空间 alert(a + 100); // 使用内存 a = null; // 使用完毕之后,释放内存空间 ``` 第一步和第二步我们都很好理解,JavaScript在定义变量时就完成了内存分配。第三步释放内存空间则是我们需要重点理解的一个点。 JavaScript有自动垃圾收集机制,那么这个自动垃圾收集机制的原理是什么呢?其实很简单,就是找出那些不再继续使用的值,然后释放其占用的内存。垃圾收集器会每隔固定的时间段就执行一次释放操作。 在JavaScript中,最常用的是通过**标记清除**的算法来找到哪些对象是不再继续使用的,因此 `a = null`其实仅仅只是做了一个释放引用的操作,让 a 原本对应的值失去引用,脱离执行环境,这个值会在下一次垃圾收集器执行操作时被找到并释放。而在适当的时候解除引用,是为页面获得更好性能的一个重要方式。 需要注意的是,在局部作用域中,当函数执行完毕,局部变量也就没有存在的必要了,因此垃圾收集器很容易做出判断并回收。但是全局变量什么时候需要释放内存空间则很难判断,因此在我们的开发中,原则上应该避免使用全局变量。 ## 相关: 1.JavaScript垃圾回收机制:《JavaScript高级编程》中的4.3节 <span class="external-link"><a href="https://www.cnblogs.com/guoxiaoyan/p/8664150.html" target="_blank">2.什么是“堆”,"栈","堆栈","队列",它们的区别<i data-feather='external-link'></i></a></span> <span class="external-link"><a href="http://www.fly63.com/article/detial/6410" target="_blank">3.Js闭包的实现原理和作用<i data-feather='external-link'></i></a></span> Last modification:July 27th, 2020 at 04:21 pm © 允许规范转载 Support 如果觉得我的文章对你有用,请随意赞赏 ×Close Appreciate the author Sweeping payments Pay by AliPay Pay by WeChat
所以闭包的意义完全没有讲
附链讲的比较含糊,概括一下
封装性,模块化,隐藏私有状态和函数,缩短传参,隐藏细节
方便实现函数式编程和面向事件编程
闭包的意义是为了不污染变量,具体的详细解释在往后我会另起一篇文章
https://stackoverflow.com/questions/111102/how-do-javascript-closures-work
可以参考一下
好,爆栈牛逼
行我到家看看
www