如果您知道記憶體流失的起因,那麼在 JavaScript 中進行相應的防範就應該相當容易。在這篇文章中,作者 Kiran Sundar 和 Abhijeet Bhattacharya 將帶您親曆 JavaScript 中的循環參考的全部基本知識,向您介紹為何它們會在某些瀏覽器中產生問題,尤其是在結合了閉包的情況下。在瞭解了您應該引起注意的常見記憶體流失模式之後,您還將學到應對這些泄漏的諸多方法。
JavaScript 是用來向 Web 頁面添加動態內容的一種功能強大的指令碼語言。它尤其特別有助於一些日常任務,比如驗證密碼和建立動態菜單組件。JavaScript 易學易用,但卻很容易在某些瀏覽器中引起記憶體的泄漏。在這個介紹性的文章中,我們解釋了 JavaScript 中的泄漏由何引起,展示了常見的記憶體流失模式,並介紹了如何應對它們。
注意本文假設您已經非常熟悉使用 JavaScript 和 DOM 元素來開發 Web 應用程式。本文尤其適合使用 JavaScript 進行 Web 應用程式開發的開發人員,也可供有興趣建立 Web 應用程式的客戶提供瀏覽器支援以及負責瀏覽器故障排除的人員參考。
JavaScript 中的記憶體流失
JavaScript 是一種垃圾收集式語言,這就是說,記憶體是根據對象的建立分配給該對象的,並會在沒有對該對象的引用時由瀏覽器收回。JavaScript 的垃圾收集機制本身並沒有問題,但瀏覽器在為 DOM 對象分配和恢複記憶體的方式上卻有些出入。
Internet Explorer 和 Mozilla Firefox 均使用引用計數來為 DOM 對象處理記憶體。在引用計數系統,每個所引用的對象都會保留一個計數,以獲悉有多少對象正在引用它。如果計數為零,該對象就會被銷毀,其佔用的記憶體也會返回給堆。雖然這種解決方案總的來說還算有效,但在循環參考方面卻存在一些盲點。
循環參考的問題何在?
當兩個對象互相引用時,就構成了循環參考,其中每個對象的引用計數值都被賦 1。在純垃圾收集系統中,循環參考問題不大:若涉及到的兩個對象中的一個對象被任何其他對象引用,那麼這兩個對象都將被垃圾收集。而在引用計數系統,這兩個對象都不能被銷毀,原因是引用計數永遠不能為零。在同時使用了垃圾收集和引用計數的混合系統中,將會發生泄漏,因為系統不能正確識別循環參考。在這種情況下,DOM 對象和 JavaScript 對象均不能被銷毀。清單 1 顯示了在 JavaScript 對象和 DOM 對象間存在的一個循環參考。
清單 1. 循環參考導致了記憶體流失
<html>
<body>
<script type="text/javascript">
document.write("circular references between JavaScript and DOM!");
var obj;
window.onload = function(){
obj=document.getElementById("DivElement");
document.getElementById("DivElement").expandoProperty=obj;
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
};
</script>
<div id="DivElement">Div Element</div>
</body>
</html>
如上述清單中所示,JavaScript 對象 obj 擁有到 DOM 對象的引用,表示為 DivElement。而 DOM 對象則有到此 JavaScript 對象的引用,由 expandoProperty 表示。可見,JavaScript 對象和 DOM 對象間就產生了一個循環參考。由於 DOM 對象是通過引用計數管理的,所以兩個對象將都不能銷毀。