function sf() {
var num = 0
return function inner() {
console.log(num)
}
inner()
}
var fn = sf()
fn()
fn()
fn()
把一個(gè)內(nèi)部的函數(shù)枫匾,通過return的形式,拿到了全局范圍谴忧,然后就變成了全局函數(shù)角虫。本來num是一個(gè)局部變量,本來它被用完了應(yīng)該被清理均驶,但是現(xiàn)在不清理枫虏,這個(gè)num變量會(huì)一直存在,一直到全部的調(diào)用都消失了腾它,才會(huì)清理死讹。
這種情況就涉及到了垃圾回收機(jī)制的問題
畫圖說明更直觀:
去餐廳吃飯赞警,吃完飯餐桌肯定弄的亂七八糟稀并。
一種情況是按照要求单默,所有吃完飯的人忘瓦,走之前你必須給我把桌子給我收拾干凈,不然不能走境蜕。
另一種凌停,你盡管吃,別管多亂台诗,吃完你就走赐俗,旁邊會(huì)有一個(gè)專門負(fù)責(zé)打掃衛(wèi)生的保潔,然后他沒事就在這里轉(zhuǎn)悠粱快,她發(fā)現(xiàn)這個(gè)桌子吃完了叔扼,然后清理掉。這個(gè)充當(dāng)?shù)木褪抢厥掌鞯慕巧⒃邸:喎Q為GC食呻。
很顯然它是自動(dòng)的不用你管我們的JS也是這樣的,所以當(dāng)一個(gè)變量沒有用清理的時(shí)候你不必?fù)?dān)心每辟,該清理的時(shí)候自然就清理了干旧。
垃圾回收機(jī)制的常見算法
引用計(jì)數(shù)法(瀏覽器):
用的較多
計(jì)數(shù)法的意思就是給每個(gè)變量貼標(biāo)簽,只要有一個(gè)人用我就給他寫個(gè)數(shù)字:1挠将,兩個(gè)人用我就寫:2,三個(gè)人用就是:3乳丰;其中有一個(gè)人不用了我可以把技術(shù)撤掉内贮,把3改回2∈惭啵總之就是上面有個(gè)數(shù)字竞端,代表幾個(gè)人在使用。
這對于GC來說就簡單了事富,它巡視查看內(nèi)存的時(shí)候只要看這個(gè)變量的計(jì)數(shù)是不是等于0技俐,如果等于0毫不猶豫的清理掉,不等于0說明還有人在使用就不能清理统台。
這種方式容易產(chǎn)生很多碎片虽另,有些空間能用有些被占用了,不會(huì)連續(xù)饺谬,我們說硬盤里時(shí)不時(shí)要做碎片整理捂刺,就是為了讓我們硬盤空間利用效率更高,所以說最好就是時(shí)不時(shí)的把空間規(guī)整一下募寨,排個(gè)序族展。
復(fù)制整理法(Java內(nèi)存管理):
不停在用,整個(gè)過程都不清理拔鹰。一口氣把所有的已經(jīng)占用對的空間復(fù)制一遍,它在整個(gè)復(fù)制的過程中把所有的內(nèi)存掃描了一遍列肢,復(fù)制的時(shí)候把沒用的清理了恰画,而且做了重新排序。
缺點(diǎn):你的內(nèi)存必須得一分為二瓷马,可能有一半空間不能利用拴还。如果你內(nèi)存大不在乎,這種方式是不錯(cuò)的選擇
標(biāo)記清除法(瀏覽器):
用的不多
不停的增加欧聘,每隔一段時(shí)間就使勁的掃描內(nèi)存片林,并且清理,過一段時(shí)間再掃描,和引用計(jì)數(shù)法一樣费封,同樣會(huì)存在碎片
標(biāo)記整理法(瀏覽器):
用的較多
當(dāng)變量越來越多的時(shí)候焕妙,它也會(huì)定期的進(jìn)行掃描,掃描完了它從頭到尾做了一個(gè)排序弓摘,一邊清除焚鹊,一邊排序,工作量蠻大的
閉包中的變量num為什么不會(huì)被回收韧献?
明白了垃圾回收機(jī)制寺旺,那么閉包中的num為什么不會(huì)被回收,應(yīng)該就可以解釋了吧势决!外面的全局函數(shù)還在使用num,因此num身上有計(jì)數(shù)蓝撇,不為零果复,所以它不會(huì)被清除。
內(nèi)存泄漏常見情況
- 閉包寫多了就會(huì)造成內(nèi)存泄漏渤昌,一般不可能代碼里全是閉包虽抄,我們只能說有這個(gè)風(fēng)險(xiǎn)
- 大量的使用全局變量,現(xiàn)在基本不會(huì)出現(xiàn)独柑,因?yàn)槲覀儸F(xiàn)在都模塊化了迈窟,全局變量很少用
- 定時(shí)器,這個(gè)可能性大一些忌栅,我們總是開啟了就忘記了關(guān)閉车酣,經(jīng)常犯
- DOM元素的引用:
<body>
<button id = 'btn'>按鈕</button>
</body>
<script>
var btn = document.getElementById('btn')
btn.remove() // 這個(gè)按鈕就不會(huì)出現(xiàn)在頁面上了
// 但是你在控制臺(tái)(console.log())輸入btn ,結(jié)果就是還有
// 為什么還有索绪?
// 頁面是不存在了湖员,但是頁面上還有
// 就是因?yàn)槿肿兞?btn ,btn.remove刪除了節(jié)點(diǎn)瑞驱,但是它就還被保留在內(nèi)存當(dāng)中
//解決方案:
</script>
解決方案
btn = null // 現(xiàn)在被釋放了