標籤:建立過程 建立 win 經曆 代碼執行 執行 就會 efi size
一個熱愛技術的菜鳥...用點滴的積累鑄就明日的達人
本文
在上一篇文章中講解了JavaScript記憶體模型,其中有提到執行內容與變數對象的概念。對於JavaScript開發人員來說,理解執行內容與變數對象的基本理論知識,是理解閉包,原型鏈的關鍵所在(閉包與原型鏈會在接下來的文章中介紹)。本篇文章就帶你走進JavaScript的執行內容與變數對象,由於本人才疏學淺,若有什麼表述有誤的地方,歡迎各位看官能夠指點一二,在此不勝感激...
在閱讀這邊文章之前,預設您已經掌握了JavaScript的基本概念、棧堆等基本資料結構以及電腦基本理論基礎,如有瞭解欠缺,請移步相關部落格後再閱讀本文。
一、執行內容
JavaScript執行程式的時候,會首先建立一個全域的執行內容,將其放入棧中,成為棧底。而後每次執行一個函數的時候,都會去建立一個當前函數的執行內容,然後將其壓入棧中,當函數結束或者返回的時候,再從棧中彈出當前函數的執行內容。執行內容其實就是函數的執行環境,與Java中的棧幀很像,但是與Java不同的是,由於JavaScript是單線程的,所以JavaScript只有一個棧,對所有的執行內容壓棧與出棧都是相對與同一個棧。
執行內容的生命週期包含三個部分:建立階段、代碼執行階段,執行完畢階段
- 建立階段:在這個階段中,執行內容會分別建立變數對象,建立範圍鏈,以及確定this的指向
- 代碼執行階段:建立完成之後,就會開始執行代碼,這個時候,會完成變數賦值,函數引用,以及執行其他代碼。
- 執行完畢階段:執行完畢之後,就會開始將執行內容出棧,隨後釋放掉所佔用的記憶體
下面用圖解的方式來描述一下執行內容的壓棧與出棧的步驟,首先先看一段程式
function method_a() { method_b(); method_c();}function method_b() {}function method_c() { method_d();}function method_d() {}method_a();
在開始執行程式的時候,會首先建立一個global的全域執行內容,將其壓入棧中
隨後執行method_a()的時候,會建立一個method_a的執行內容,將其壓入棧中
而後,開始執行method_a()函數中的代碼,當執行到第一行的時候,發現要執行method_b()這個函數,所以又開始建立一個method_b的執行內容,將其壓入棧中
順利的執行完method_b()函數代碼之後,將method_b的執行環境出棧,隨後繼續執行method_a()函數剩下的代碼,發現要執行method_c()函數,所以繼續建立method_c的執行內容,將其壓入棧中
在執行method_c()函數的時候,發現又要執行method_d()函數,所以又要建立method_d的執行內容,隨後執行完method_d()之後,將其出棧,再之後method_c出棧,method_a出棧,最後剩餘global執行內容
至此,JavaScript中的執行內容概念介紹完畢...
二、變數對象(VO)
在介紹執行內容的建立階段的時候,有提到變數對象這個概念...本節先來重點瞭解一下變數對象。變數對象的建立,依次經曆了以下幾個過程。
- 建立arguments對象(參數對象,當某個函數接收參數的時候,會將參數封裝成arguments對象)
- 檢查當前內容相關的函式宣告,也就是使用function聲明的函數。在變數對象中以函數名建立一個屬性,屬性值為指向該函數所在的記憶體位址的引用。如果函數名的屬性已存在,那麼該屬性將會被新的引用所覆蓋。
- 檢查當前內容相關的變數聲明,對於每一個變數聲明,變數對象會以變數名建立一個屬性,屬性值為undefined。如果變數名屬性已存在,則會直接跳過,原屬性值保持不變(這樣是為了防止將同名的函數名覆蓋)
在上面的規則中可以看出,function聲明會比var聲明優先順序更高一點,接下來看一個demo,藉此來學習與驗證一下上述命題
function method(num1, num2) { console.log(arguments); console.log(num1, num2); console.log(vara, func); console.log(func()); var vara = 10; console.log(vara); function func() { return 1; } function func() { return 2; }}method(1, 2);
輸出結果如下
{ ‘0‘: 1, ‘1‘: 2 }1 2undefined [Function: func]210
首先輸出的是arguments對象,通過與第二行輸出num1,num2對比可知,argument對象是一種key-value格式的對象,其中key從0開始逐步遞增,value依次對應著傳入參數。
隨後根據變數聲明與函式宣告的規則,由於變數聲明,會在執行內容建立階段為變數賦值undefined,而函式宣告,會在執行內容建立階段為函數變數賦值指向該函數的引用地址。故第三行輸出結果為 undefined 與 [Function:func]
又由於代碼中聲明了兩個func函數,為了驗證是否存在函數覆蓋現象,列印輸出func()的結果,輸出結果為2,則意味著,第二個定義的func函數,在執行內容階段,覆蓋了一開始的func函數。
最後給vara賦值10,隨即輸出vara,得到10
三、OV與AO
在初學JavaScipt中,總會有同學對變數對象與使用中的物件弄混,為此解釋一下這兩個名詞。在執行內容建立的階段會建立變數對象,變數對象中儲存arguments,function聲明的函數,var聲明的變數,具體的第二節已經介紹,在此不做過多闡述。當執行內容建立過程的結束,就開始了代碼執行階段,在此時JavaScript會將變數對象(VO)轉換成使用中的物件(AO)。在未進入到執行階段之前,變數對象中的屬性都不能訪問,但是進入執行階段後,由於變數對象被轉換成了使用中的物件,裡面的屬性就可以被訪問了,隨後開始執行代碼。因此在第二節的例子上,其實輸出的都是使用中的物件的屬性(但變數對象與使用中的物件中包含的屬性與值都相同,所以姑且認為是變數對象)
再總結一下,其實變數對象與使用中的物件都是同一個對象,只是它們處於不同的執行內容生命週期。
四、全域變數對象
以瀏覽器為例,全域變數對象為window對象。除此之外,全域變數對象的生命週期與執行內容一致,所以如果瀏覽器視窗如果不關閉的話,全域變數對象將會一直存在。
JavaScript學習系列之執行內容與變數對象篇