1、結構順序大致介紹web
JavaScript代碼的整個執行過程,分爲兩個階段,代碼編譯階段與代碼執行階段。瀏覽器
編譯階段由編譯器完成,將代碼翻譯成可執行代碼,這個階段做用域規則會肯定。函數
執行階段由引擎完成,主要任務是執行可執行代碼,執行上下文在這個階段建立。執行上下文也分爲建立階段和執行階段。
this
1.首先進入全局環境,建立一個全局執行上下文,全局變量對象window,全局做用域Global,肯定this指向,this==window。spa
2.在執行階段,會完成變量賦值,函數引用,以及執行其餘代碼等。瀏覽器的斷點調試只能用於執行階段。線程
3.在執行階段,若是遇到函數引用,會進入函數環境,建立一個執行上下文。而在這個執行上下文中,也分爲建立階段和執行階段。接下來咱們主要討論函數環境的建立階段和執行階段,不考慮全局環境,由於全局環境也能夠理解爲在一個大的函數環境中。行爲基本一致。翻譯
2、執行上下文調試
每次當Js引擎轉到可執行代碼的時候,就會進入一個執行上下文。執行上下文能夠理解爲當前代碼的執行環境,它會造成一個做用域。JavaScript中的運行環境大概包括三種狀況。code
當代碼在執行過程當中,遇到以上三種狀況,都會生成一個執行上下文,放入棧中,咱們稱其爲函數調用棧(call stack)。棧底永遠都是全局上下文,而棧頂就是當前正在執行的上下文。處於棧頂的上下文執行完畢以後,就會自動出棧。對象
注意:函數中,遇到return能直接終止可執行代碼的執行,所以會直接將當前上下文彈出棧。
執行上下文特色:
• 單線程
• 同步執行,只有棧頂的上下文處於執行中,其餘上下文須要等待
• 全局上下文只有惟一的一個,它在瀏覽器關閉時出棧
• 函數的執行上下文的個數沒有限制
• 每次某個函數被調用,就會有個新的執行上下文爲其建立,即便是調用的自身函數,也是如此。
當調用一個函數時,一個新的執行上下文就會被建立。而一個執行上下文的生命週期能夠分爲兩個階段。
建立階段
:在這個階段中,執行上下文會分別建立變量對象,創建做用域鏈,以及肯定this的指向。
代碼執行:階段
建立完成以後,就會開始執行代碼,這個時候,會完成變量賦值,函數引用,以及執行其餘代碼。
3、變量對象
在執行上下文的建立階段,會進行變量對象的建立。
而變量對象的建立過程又分爲:
1.參數對象建立。創建arguments對象,檢查當前上下文中的參數,創建該對象下的屬性與屬性值。
2.函數聲明。檢查當前上下文的函數聲明,也就是使用function關鍵字聲明的函數。在變量對象中以函數名創建一個屬性,屬性值爲指向該函數所在內存地址的引用。若是函數名的屬性已經存在,那麼該屬性將會被新的引用所覆蓋。
function聲明會比var聲明優先級更高一點。
3.變量聲明。檢查當前上下文中的變量聲明,每找到一個變量聲明,就在變量對象中以變量名創建一個屬性,屬性值爲undefined。若是該變量名的屬性已經存在,爲了防止同名的函數被修改成undefined,則會直接跳過,原屬性值不會被修改。
在執行上下文的執行階段,進行變量對象的賦值,函數的引用等。
進入執行階段以前,變量對象中的屬性都不能訪問!可是進入執行階段以後,變量對象轉變爲了活動對象,裏面的屬性都能被訪問了,而後開始進行執行階段的操做。
變量對象和活動對象其實都是同一個對象,只是處於執行上下文的不一樣生命週期。
4、舉個例子:
function test() { var a = 1; function foo() { return 2; } } test();
咱們直接從test()的執行上下文開始理解。全局做用域中運行test()
時,test()的執行上下文開始建立。爲了便於理解,咱們用以下的形式來表示。
建立階段:
testEC = {
// 變量對象 VO: {}, scopeChain: {}, this: {} } // 由於本文暫時不詳細解釋做用域鏈和this,因此把變量對象專門提出來講明 // VO 爲 Variable Object的縮寫,即變量對象 VO = { arguments: {...}, //參數對象。注:在瀏覽器的展現中,函數的參數可能並非放在arguments對象中,這裏爲了方便理解,我作了這樣的處理 foo: <foo reference> // 表示foo的地址引用 a: undefined }
執行階段:
VO -> AO // Active Object 即活動對象 AO = { arguments: {...}, foo: <foo reference>, a: 1 }
5、變量提高
變量提高是將變量聲明提高到它所在做用域的最開始的部分。如:
console.log(a); //undefined
console.log(b); //Uncaught ReferenceError: b is not defined
var a = 1;
代碼執行分爲建立和執行兩個階段,代碼開始執行的時候,建立階段已經完成,因此變量對象已經建立完成。此時a是undefined,而b是沒有的。
因此也就解釋了變量提高的現象,由於在執行的時候,全部的變量對象早已建立完成,只是尚未被賦值。