JavaScript內存管理與閉包實例詳解
1. 內存管理的理解
1.1 認識內存管理
不管什么樣的編程語言,在代碼的執(zhí)行過程中都是需要給它分配內存的,不同的是某些編程語言需要我們自己手動的管理內存,某些編程語言會可以自動幫助我們管理內存.
不管以什么樣的方式來管理內存,內存的管理都會有如下的生命周期:
分配申請你需要的內存(申請)
使用分配的內存(存放一些東西,比如對象等)
不需要使用時,對其進行釋放
不同的編程語言對于第一步和第三步會有不同的實現:
手動管理內存:比如C、C++,包括早期的OC,都是需要手動來管理內存的申請和釋放的(malloc和free函數)
自動管理內存:比如Java、JavaScript、Python、Swift、Dart等,它們會自動管理內存
1.2 JavaScript的內存管理
JavaScript 的內存管理是自動的、無形的:創(chuàng)建的原始值、對象、函數……這一切都會占用內存
JS對于原始數據類型內存的分配會在執(zhí)行時,直接在??臻g進行分配
JS對于復雜數據類型內存的分配會在堆內存中開辟一塊空間,并且將這塊空間的指針返回值變量引用
2. 垃圾回收(GC)
2.1 認識垃圾回收
因為內存的大小是有限的,所以當內存不再需要的時候,需要對其進行釋放,以便騰出更多的內存空間。
大部分現代的編程語言都是有自己的垃圾回收機制:
垃圾回收的英文是Garbage Collection,簡稱GC
對于那些不再使用的對象,都稱之為是垃圾,它需要被回收,以釋放更多的內存空間
而我們的語言運行環(huán)境,比如Java的運行環(huán)境JVM,JavaScript的運行環(huán)境js引擎都會使用垃圾回收器(GC)
2.2 GC算法 – 引用計數
引用計數:
當一個對象有一個引用指向它時,那么這個對象的引用就+1
當一個對象的引用為0時,這個對象就可以被銷毀掉
這個算法有一個很大的弊端就是會產生循環(huán)引用:

2.3 GC算法 – 標記清除
標記清除:
標記清除的核心思路是可達性(Reachability)
這個算法是設置一個根對象(root object)[在js中指window],垃圾回收器會定期從這個根開始,找所有從根開始有引用到的對象,對于那些沒有引用到的對象,就認為是不可用的對象
這個算法可以很好的解決循環(huán)引用的問題

2.4 其他算法優(yōu)化補充
JS引擎比較廣泛的采用的就是可達性中的標記清除算法,當然類似于V8引擎為了進行更好的優(yōu)化,它在算法的實現細節(jié)上也會結合一些其他的算法。
標記整理(Mark-Compact) 和“標記-清除”相似
- 不同的是,回收期間同時會將保留的存儲對象搬運匯集到連續(xù)的內存空間,從而整合空閑空間,避免內存碎片化
分代收集(Generational collection)—— 對象被分成兩組:“新的”和“舊的”
許多對象出現,完成它們的工作并很快死去,它們可以很快被清理
那些長期存活的對象會變得“老舊”,而且被檢查的頻次也會減少
增量收集(Incremental collection)
如果有許多對象,并且我們試圖一次遍歷并標記整個對象集,則可能需要一些時間,并在執(zhí)行過程中帶來明顯的延遲。
所以引擎試圖將垃圾收集工作分成幾部分來做,然后將這幾部分會逐一進行處理,這樣會有許多微小的延遲而不是一個大的延遲
閑時收集(Idle-time collection)
- 垃圾收集器只會在 CPU 空閑時嘗試運行,以減少可能對代碼執(zhí)行的影響
3. 閉包的概念理解
3.1 JavaScript的函數式編程
在JavaScript中,函數是非常重要的,并且是一等公民:
那么就意味著函數的使用是非常靈活的
函數可以作為另外一個函數的參數,也可以作為另外一個函數的返回值來使用
JavaScript存在很多的高階函數:
自己編寫高階函數
使用內置的高階函數
在vue3+react開發(fā)中,也都在趨向于函數式編程:
vue3 composition api: setup函數 -> 代碼(函數hook,定義函數)
react:class -> function -> hooks
3.2 定義
在計算機科學中對閉包的定義(維基百科):
閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函數閉包(function closures)
是在支持 頭等函數 的編程語言中,實現詞法綁定的一種技術
閉包在實現上是一個結構體,它存儲了一個函數和一個關聯的環(huán)境(相當于一個符號查找表)
閉包跟函數最大的區(qū)別在于,當捕捉閉包的時候,它的 自由變量 會在捕捉時被確定,這樣即使脫離了捕捉時的上下文,它也能照常運行
閉包的概念出現于60年代,最早實現閉包的程序是 Scheme,那么我們就可以理解為什么JavaScript中有閉包:因為JavaScript中有大量的設計是來源于Scheme的
MDN對JavaScript閉包的解釋:
一個函數和對其周圍狀態(tài)(lexical environment,詞法環(huán)境)的引用捆綁在一起(或者說函數被引用包圍),這樣的組合就是閉包(closure)
也就是說,閉包讓你可以在一個內層函數中訪問到其外層函數的作用域
在 JavaScript 中,每當創(chuàng)建一個函數,閉包就會在函數創(chuàng)建的同時被創(chuàng)建出來
總結:
一個普通的函數function,如果它可以訪問外層作用域的自由變量,那么這個函數和周圍環(huán)境就是一個閉包
從廣義的角度來說:JavaScript中的函數都是閉包
從狹義的角度來說:JavaScript中一個函數,如果訪問了外層作用域的變量,那么它是一個閉包
4. 閉包的內存流程
function createAdder(count) {
function adder(num) {
return count + num
}
return adder
}
var adder5 = createAdder(5)
adder5(100)
adder5(55)
adder5(12)
var adder8 = createAdder(8)
adder8(22)
adder8(35)
adder8(7)- 第一次調用createAdder

- 調用createAdder完成

- 內部adder執(zhí)行完成

- 第二次執(zhí)行createAdder

5. 閉包的內存泄漏
5.1 認識內存泄露
閉包是有內存泄露的
在上面的案例中,如果后續(xù)我們不再使用adder8函數了,那么該函數對象應該要被銷毀掉,并且其引用著的父作用域AO也應該被銷毀掉
但是目前因為在全局作用域下adder8變量對0xc00的函數對象有引用,而0xc00的作用域中AO(0x300)有引用,所以最終會造成這些內存都是無法被釋放的
閉包會造成內存泄露,其實就是剛才的引用鏈中的所有對象都是無法釋放的
解決閉包的內存泄露
當將adder8設置為null時,就不再對函數對象0xc00有引用,那么對應的AO對象0x300也就不可達了
在GC的下一次檢測中,它們就會被銷毀掉

5.2 內存泄露的測試
<button class="create">創(chuàng)建一系列的數組對象</button>
<button class="destroy">銷毀一系列的數組對象</button>
<script>
function createArray() {
// 4 1024 -> 4kb * 1024 -> 4M
var arr = new Array(1024*1024).fill(1)
function test() {
console.log(arr)
}
return test
}
// 點擊按鈕
var totalArr = []
var createBtnEl = document.querySelector(".create")
var destroyBtnEl = document.querySelector(".destroy")
createBtnEl.onclick = function() {
for (var i = 0; i < 100; i++) {
totalArr.push(createArray())
}
console.log(totalArr.length)
}
destroyBtnEl.onclick = function() {
// 釋放
totalArr = []
}
</script>- 創(chuàng)建數組對象(占用內存)

- 銷毀數組對象(釋放內存)

5.3 瀏覽器的優(yōu)化
function foo() {
var name = "foo"
var age = 18
var height = 1.88
function bar() {
debugger
console.log(name)
}
return bar
}
var fn = foo()
fn()
總結
到此這篇關于JavaScript內存管理與閉包的文章就介紹到這了,更多相關js內存管理與閉包內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JavaScript操作XML/HTML比較常用的對象屬性集錦
本文給大家介紹javascript操作xml/html比較常用的對象屬性,涉及到js對象屬性相關知識,對JavaScript操作XML/HTML比較常用的對象屬性感興趣的朋友可以參考下本文2015-10-10
Bootstrap Modal對話框如何在關閉時觸發(fā)事件
這篇文章主要為大家詳細介紹了Bootstrap Modal對話框如何在關閉時觸發(fā)事件,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12

