隨著 Web 應(yīng)用復(fù)雜程度越來越高,以及 NodeJS 大規(guī)模投入生產(chǎn)環(huán)境,許多 Web 應(yīng)用都會(huì)長時(shí)間運(yùn)行, JAVAScript 的內(nèi)存管理顯得更為重要。
JavaScript 具備自動(dòng)回收垃圾的機(jī)制, 執(zhí)行環(huán)境會(huì)負(fù)責(zé)管理代碼在執(zhí)行環(huán)境過程中使用的內(nèi)存,將某些不再被使用的的變量所占用的內(nèi)存釋放掉,正因如此,大多數(shù)情況我們在前端開發(fā)的時(shí)候,并不是那么關(guān)注我們的頁面用了多少內(nèi)存,是否合理,需不需要優(yōu)化。
JavaScript 基礎(chǔ)中有很多重要的知識(shí)點(diǎn)是和內(nèi)存相關(guān)的,比如深拷貝和淺拷貝、閉包、原型、引用數(shù)據(jù)類型和引用傳遞等。
當(dāng)然,關(guān)于 JS 的內(nèi)存空間和內(nèi)存相關(guān)的知識(shí)已經(jīng)有很多專業(yè)的文章解釋的很詳細(xì)了,這里就不再贅述了。
比如關(guān)于 JS 內(nèi)存空間的知識(shí)可以看看:內(nèi)存空間詳細(xì)圖解
關(guān)于內(nèi)存周期和垃圾回收的知識(shí)可以閱讀參考 MDN 的文章,其他文章無外乎也是根據(jù)這個(gè)來介紹的:Memory_Management
關(guān)于 JavaScript 內(nèi)存泄漏也可以看一下阮一峰老師的文章 JavaScript 內(nèi)存泄漏教程
在 Web 應(yīng)用開發(fā)中,我們應(yīng)該注意:
1. 避免沒有必要全局變量的使用
前端開發(fā)者都知道,在局部作用域中,當(dāng)函數(shù)執(zhí)行完畢,局部變量也就沒有存在的必要了,它很容易被垃圾回收器回收,當(dāng)使用全局變量定義值時(shí),垃圾回收器,很難判斷全局變量需要什么時(shí)候釋放內(nèi)存空間,因此是不會(huì)去對其進(jìn)行回收銷毀的。而該變量會(huì)一直存在老生代堆內(nèi)存中,直到頁面被關(guān)閉。
function setName () { name = "alloy"; } // 等價(jià) function setName () { window.name = "alloy" }
另外一種意外情況是;
function setName (name) { this.name = name; } setName("alloy")
我們可以在 JS 文件的開頭通過添加"use strict" 開啟嚴(yán)格的解析模式,來避免一些意外創(chuàng)建的全局變量。
2. 及時(shí)解除引用
如果必須要一個(gè)全局變量來存儲(chǔ)大量數(shù)據(jù),那么請確保在用完之后將其賦值為 null。
delete 操作符用于刪對象的某個(gè)屬性;如果沒有指向這個(gè)屬性的引用,那它最終會(huì)釋放。
但注意的一點(diǎn)是,盡量不要在需要密集運(yùn)算的函數(shù)中去使用 delete,這很可能會(huì)引發(fā)瀏覽器在不恰當(dāng)?shù)臅r(shí)候的 GC,和其他語言一樣,JavaScript 的 GC 策略無法避免 GC 時(shí)停止響應(yīng)其他操作,而 JavaScript 的 GC 在 50ms 甚至以上,對普通應(yīng)用還好,如果是對于操作頻繁的 Web 應(yīng)用或者游戲來說,就比價(jià)煩惱了。
const Room = { desks: 10, chairs: 22 }; console.log(Room.desks); // 10; delete Row.desks; console.log(Room.desks); // undefined
有時(shí)候我們雖然用 removeChild 移除了 button,但是還在 node 對象里保存著 #button 的引用,DOM 元素還在內(nèi)存里面,需要及時(shí)解除引用。
var node = { button: document.getElementById('button'); }; document.body.removeChild(document.getElementById('button'));
3. 減少對象的創(chuàng)建
垃圾回收周期性運(yùn)行,如果分配的內(nèi)存非常多,或者新建很很多實(shí)例的話,那么回收工作也會(huì)很辛苦。
盡量避免在經(jīng)常調(diào)用的方法中循環(huán)使用 new 對象,而且還要花時(shí)間對這些對象進(jìn)行垃圾回收和處理。
設(shè)計(jì)模式中的享元模式就是為了減少對象的多次創(chuàng)建而來的。在我們可以控制的范圍內(nèi),最大限度的重用對象。
4. 內(nèi)存不是緩存
緩存在需求開發(fā)中舉足輕重,但是很多時(shí)候我們會(huì)把許多大數(shù)據(jù)緩存在內(nèi)存中,導(dǎo)致我們的內(nèi)存占用始終處于高位,內(nèi)存對任何程序開發(fā)都是寸土寸金 的,若果不是很重要的資源,請不要直接放在內(nèi)存中,或者制定過期機(jī)制,自動(dòng)銷毀過期緩存。
5. 避免復(fù)雜的遞歸調(diào)用;
通常情況下,簡單的遞歸調(diào)用還不至于導(dǎo)致堆棧溢出,但遇到復(fù)雜且每次調(diào)用需要 在棧里存儲(chǔ)大量信息的時(shí)候,成千上萬個(gè)此類空間累積起來,很容易就超過了棧空間。
6. 合理使用的 IndexedDB
其實(shí)這個(gè)是和 JS 關(guān)系不是很大,但是對于 Web 應(yīng)用的影響卻十分重要,曾經(jīng)我遇到過一個(gè)用戶案例,由于長時(shí)間的本地?cái)?shù)據(jù)寫入,和一些上報(bào)日志沒有被及時(shí)清除,導(dǎo)致用戶的瀏覽器中對應(yīng)域名下的 IndexedDB 存儲(chǔ)高達(dá) 12GB,瀏覽器在訪問對應(yīng)域名的時(shí)候,也可以初始化 IndexedDB 和讀取本地存儲(chǔ)的數(shù)據(jù),而面對如此龐大的數(shù)據(jù),瀏覽器內(nèi)存暴漲,最后崩潰,避免過度依賴 IndexedDB,無腦寫入數(shù)據(jù)而不做定期清理。
總結(jié)
這篇分享主要總結(jié)了我們在 Web 應(yīng)用,可能會(huì)遇到的一些情況和注意的事情。
很多時(shí)候只要我們在編碼的時(shí)候多加注意,可以避免很多問題。
希望本文能幫助到您!
點(diǎn)贊+轉(zhuǎn)發(fā),讓更多的人也能看到這篇內(nèi)容(收藏不點(diǎn)贊,都是耍流氓-_-)
關(guān)注 {我},享受文章首發(fā)體驗(yàn)!
每周重點(diǎn)攻克一個(gè)前端技術(shù)難點(diǎn)。更多精彩前端內(nèi)容私信 我 回復(fù)“教程”!希望本文能幫助到您!
轉(zhuǎn)載自AlloyTeam:http://www.alloyteam.com/2019/07/13858/