也許不少人像我同樣,以爲JS有垃圾回收機制,內存就能夠無論了,以致於在全局做用域下定義了不少變量,自覺得JS會自動回收,直到最近,看了阮一峯老師,關於javascript內存泄漏的文章時,才發現本身寫的代碼,存在很嚴重的內存泄漏問題,再者,由於忽略對內存的學習,致使後面不少進階概念很模糊,好比深複製與淺複製的區別,好比閉包、做用域鏈等等。javascript
與C/C++不一樣,JavaScript語言沒有嚴格意義上,區分堆與棧,因此咱們能夠理解爲,JavaScript全部的數據都是存放在堆內存
中。
不過,在某些場景下,咱們仍然須要藉助堆棧數據結構來處理,因此有必要理解一下這兩個數據結構的區別。java
棧:也叫作堆棧程序員
棧數據結構的一個特色就是後進先出
,比如羽毛球盒子,在一頭放羽毛球,在另一頭取羽毛球。
堆數據結構,比如書架上的書,雖然已經按順序放好了,可是咱們只要知道書的名字,就能夠對應的取下來,相似於JSON對象中的key-value
。數據結構
JavaScript中的數據類型大體分爲,基本數據類型
與引用數據類型
,上文提到,JavaScript中全部的數據都是存放在堆內存
中,可是,這裏提到的變量對象
(在執行上下文建立階段生成),因爲它有特殊的職能,因此在理解上就把它與堆內存單獨分開了,以下圖所示:閉包
通常變量對象
裏面存放的是基本數據類型
,包括Undefined、Null、Boolean、Number、String
,它們到是按值訪問
的。函數
demo01 var a = 20; var b = a; b = 30; console.log(a);//20
上面這段代碼指的是,在變量對象中執行數據複製的時候,其實系統會自動爲新的變量分配一個新的值,因此a與b其實已是徹底獨立的兩個變量,只是值同樣而已。學習
javascript中的引用數據類型
是存放在堆內存
中的,可是不一樣於變量對象
,javascript是不容許直接訪問堆內存中的數據,因此若是咱們要訪問引用數據類型的時候,採用的是按引用訪問
,其實就是在變量對象
中存放了一個指向對象的句柄,能夠理解爲一個地址,要訪問堆內存中的對象,就要經過這個引用句柄來訪問,例如上圖中的d變量,就是一個指向對象的地址。人工智能
demo02 var m = { a:10,b:20}; var n = m; n.a = 15; console.log(m.a);//15
上面這段代碼指的是執行引用類型數據的複製時,在變量對象中會分配一個新的值,來存放新的變量,可是這兩個變量的地址是同樣的,至關於指向的對象是同樣的,因此各自改變對象裏面的屬性值,會互相影響,以下圖code
上面講解了JavaScript中的內存空間,接下來就要講解,我寫這篇文章的初衷,就是我代碼中嚴重的內存泄漏
對象
內存泄漏:就是再也不用到的內存,可是沒有及時釋放,就叫作內存泄漏
有些語言必須手動釋放內存,程序員負責內存的管理,例如C語言
char *buffer; buffer = (char*) malloc(42); //do something with buffer free(buffer);
這裏malloc
就是負責分配內存,free
是負責釋放內存。
那麼JavaScript中的垃圾回收機制又是怎麼一回事呢?
之前我一直天真的覺得,垃圾回收機制就像人工智能同樣,會自動幫你識別出不用的內存,而後釋放掉,然而真相只有一個
垃圾回收機制的原理就是,使用引用計數
法,就是語言引擎有一張「引用表」,保存了內存裏面全部的資源的引用次數,就像下面這樣
可是若是一個值再也不須要了,引用數卻不爲0,垃圾回收機制是沒法釋放這塊內存,從而致使``內存泄漏```
例如:
const arr = [1,2,3,4,5]; console.log('hello world");
arr的引用次數爲1,儘管後面再也不使用arr了,可是它還會持續佔用內存,因此通常要這樣處理
const arr = [1,2,3,4,5]; console.log('hello world"); arr = null;
讓arr的指向爲空,垃圾回收機制就會默認它的引用數爲0而回收掉。
在局部做用域中,等函數執行完畢,變量就沒有存在的必要了,js垃圾回收機制很快作出判斷而且回收,可是對於全局變量,很難判斷何時不用,因此,經驗之談就是,儘可能少使用全局變量。 咱們在使用閉包的時候,就會形成嚴重的內存泄漏,由於閉包的緣由,局部變量會一直保存在內存中,因此在使用閉包的時候,要多加當心。