JavaScript學習系列之內存模型篇


一個熱愛技術的菜鳥...用點滴的積累鑄就明日的達人

正文
  如果真的想學好一門語言,那么一定要了解它內存模型,本篇文章就帶你走進JavaScript的內存模型,由於本人才疏學淺,若有什么表述有誤的地方,歡迎各位看官能夠指點一二,在此不勝感激...
  在閱讀這邊文章之前,默認您已經掌握了JavaScript的基本概念、棧堆等基本數據結構以及計算機基本理論基礎,如有了解欠缺,請移步相關博客后再閱讀本文。
一、基本的數據類型的內存結構
  首先粗略的介紹一下JavaScript中五種基本的數據類型Undefined、Null、Boolean、Number、String;其中對於Undefined與Null的區別,網上有很多大牛都有介紹,在此本文暫不涉及,如有疑慮之處,請移步相關博客。或許有看官會問為什么在介紹JavaScript內存模型之前要先介紹JavaScript的基本數據類型的內存結構呢?這是因為JavaScript內存模型與基本數據類型的內存結構的關系就好比數學與實數的關系,基本數據類型的內存結構是整個JavaScript內存模型的基礎。那么接下來就讓我以最簡短的方式來闡述一下基本數據類型的內存結構吧~
  基本數據類型的內存結構:在JavaScript中基本的數據類型都是以值的形式保存在內存中的。舉個例子:
 
var inta = 10;
var strb = 'Hello';

  那么在執行完這段JavaScript代碼之后,內存中會有兩個區域分別表示為inta,strb;其中表示inta區域的值為‘10’,表示strb區域的值為‘Hello’,也即表示inta與strb的內存區域保存的均為實際的真值; 

二、引用數據類型的內存結構

  在JavaScript中除了基本數據類型,那就剩下引用數據類型了,所以在介紹玩基本數據類型內存結構之后,就很有必要再介紹一下引用數據類型內存結構。引用數據類型的真實對象是保存在堆內存中的,而JavaScript與Java相似,均不可以直接訪問堆內存,所以都是使用“引用”這個東西來訪問處於堆中的對象,引用與對象的關系可以描述成遙控器與電視機之間的關系,我們可以持有遙控器來操控電視機。所謂的引用其實就是一塊內存的地址,即在表示引用的區域上保存的是內存中對象的內存地址值,如圖所示:

  其中假設對象處於內存中一個位置叫做0x23215的區域,那么橢圓的區域表示這個對象的引用,橢圓區域中存的就是0x23125這個值,在實際的操作中執行環境會通過引用中存的0x23125,去找到內存中的這個對象。

三、內存模型

  在JavaScript執行時期,可以將內存從邏輯上划分為兩部分:棧與堆。其中棧是在JavaScript執行時,用於儲存執行上下文(后續文章會介紹)的,而堆是存儲對象的區域。在執行上下文生成之后,會創建一個變量對象(后續文章會介紹),變量對象是一個特殊的對象,它也會存儲在堆。基本數據類型往往都會直接保存在變量對象中,而引用數據類型實際上是在變量對象中保存一個引用指向對象的地址(也即引用本身)。

 

  學習完JavaScript中的內存模型之后,請各位看官看看下面這段代碼,並且猜猜它的輸出結果,以驗證上述知識的理解程度:

var inta = 10;
var stra = 'Hello';

var obja = {a: 10, b: 'Hello'};

var intb = inta;
var strb = stra;
var objb = obja;

intb = 20;
strb = 'World';
objb.a = 20;
objb.b = 'World';

console.log(inta, intb);
console.log(stra, strb);
console.log(obja, objb);

  運行結果:

10 20
Hello World
{ a: 20, b: 'World' } { a: 20, b: 'World' }

  這其中會涉及到對象的賦值問題,在對基本數據類型賦值的時候,都是將原值賦值到新的對象上,所以改變新的對象的值,並不會影響到原值(因為它們本質上保存的是兩個值);而對引用數據類型賦值則是將引用所指向對象的地址賦值給另一個引用,而在后續操作中,如果通過新的引用去改變對象中內部的值的話,還是會影響原來的引用所指向的對象(因為它們本質上保存的是同一個對象)

四、內存回收機制

  JavaScript具有自動的垃圾回收機制,也即執行環境會負責代碼的執行過程中使用的內存,它會定期(周期性)找出哪些不再使用的對象,然后釋放其內存。目前JavaScript最常用的垃圾回收算法為標記清除算法,垃圾回收機制會通過標記的算法來決定哪些對象是不需要再次使用的,然后再進行清除,也即清理哪些被定義為垃圾的JavaScript對象。

  上述所述的不再使用的變量也就是生命周期結束的變量,當然只可能是局部變量。全局變量的生命周期直到瀏覽器卸載頁面才會結束,所以聲明一個全局變量的時候,我們一定要慎重的考慮,在使用完這個變量的對象之后,我們是否還在需要這個對象,如果不需要的話,我們應該手動的將這個變量置為空,這樣在下一次垃圾回收的時候,就能去釋放這個變量上一次指向的對象(請注意變量與對象的區別)。

  下面請各位看官see一下以下的代碼,來分析一下垃圾回收。

function fun1() {
    var obj = {name: 'csa', age: 24};
}

function fun2() {
    var obj = {name: 'coder', age: 2}
    return obj;
}

var f1 = fun1();
var f2 = fun2();

  在上述代碼中,當執行var f1 = fun1();的時候,執行環境會創建一個{name:'csa', age:24}這個對象,當執行var f2 = fun2();的時候,執行環境會創建一個{name:'coder', age=2}這個對象,然后在下一次垃圾回收來臨的時候,會釋放{name:'csa', age:24}這個對象的內存,但並不會釋放{name:'coder', age:2}這個對象的內存。這就是因為在fun2()函數中將{name:'coder, age:2'}這個對象返回,並且將其引用賦值給了f2變量,又由於f2這個對象屬於全局變量,所以在頁面沒有卸載的情況下,f2所指向的對象{name:'coder', age:2}是不會被回收的。

  由於JavaScript語言的特殊性(閉包...),導致如何判斷一個對象是否會被回收的問題上變的異常艱難,這不僅需要我們有很強的基本功,還需要在以后的項目中積累經驗。最后順便說一句,即使隨着硬件的更新換代以及垃圾回收機制的改進,我們也不應該忽視垃圾回收的基本理論,因為這是提高代碼性能的關鍵一步,只有這樣我們才能寫出那些堪稱藝術品的代碼...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM