幾乎全部的編程語言均可以存儲,訪問,修改變量,那在JavaScript中這些變量放在那裏?程序如何找到他們?編程
js被歸類於解釋執行語言,但事實上他也是一門編譯語言,由於他也要編譯,但於傳統的編譯語言不一樣,他不是提早編譯,編譯結果也不能在分佈式系統中進行移植。但js引擎編譯的步驟和傳統的編譯語言很是類似。編程語言
傳統的編譯會經歷3個步驟:分佈式
對於傳統的編譯過程,js引擎要複雜的多,js編譯發生在執行前的幾微秒,而後作好執行他的準備,而且一般立刻就會執行他。函數
說道這仍是沒有說這些變量放在那裏?下面介紹3位大佬:spa
這時候答案就浮出水面。啊,是這夥計--做用域.prototype
做用域?大哥你哪來的啊?上面提到做用域像一個容器同樣收集並維護全部標識符(變量和函數)事實上他是一個對象,收集的東西掛在它的屬性上。做用域的出生是由於函數的產生而產生的。當函數執行的前一刻的時候,會建立一個做用域,這個做用域定義了這個函數執行時的環境,函數每次執行時的做用域都是獨一無二的,由於他是對象啊,就像出生的孩子長得都不太同樣。這個做用域小名特別多,什麼執行期上下文,AO(Activation Object)對象,活動對象。做用域這幫夥計們有個頭叫--全局做用域。裏面的做用域能看到外面的,哈哈,你在我面前就是個小透明,但外的做用域可看不到裏面的。指針
在js高程裏解釋道做用域。code
做用域是因函數產生而產生的,每一個對象都有屬性和方法,函數(function)也是一種特殊的對象,函數能夠有test.name test.prototype ...這些是能夠訪問的,還有一些屬性是不能夠訪問的隱式屬性僅供JavaScript引擎處理。 好比[[scope]]:指的是做用域鏈,其中存儲了執行期上下文的集合。這個集合呈現鏈式鏈接,咱們把這種鏈接叫作做用域鏈。做用域鏈本質上是一個指向變量對象的指針列表,他只是引用,但不包含實際變量對象。.[[scope]]這裏面存的就是做用域。系統會根據內部的原理去按期調用scope。當函數執行的前一刻的時候,會建立一個稱爲執行期上下文的內部對象(AO activation object)。一個執行期上下文定義了一個函數執行時的環境。函數每次執行時對應的上下文都是獨一無二的,即便執行同樣的函數可是執行期上下文並不相同,因此屢次調用一個函數會致使建立多個執行上下文,當函數執行完畢,他所產生的執行上下文會銷燬。對象
上面還提到了編譯,說道js編譯發生在執行前的幾微秒。也講到變量和函數會放到做用域的問題,事實上,是這樣的,在程序執行前不是要進行編譯的嗎,在引擎和編譯器兩位大哥的幫助下 ,在編譯時(函數執行前)會處理聲明的變量和函數,把他掛到做用域這個對象的屬性上。這個過程叫 -- 見下行。blog
預編譯
當你看見var a = 2;這段程序時,極可能認爲這是一句聲明,事實上咱們引擎這哥們認爲有兩個徹底不一樣的聲明,一個由編譯器在編譯時處理,另外一個在引擎運行時處理。
代碼執行前會對其進行編譯,首先編譯器會分詞,而後解析成語法樹,最後進行代碼生成,別忘了代碼生成就是將語法樹轉化爲一組機器指令。
也就是說變量和函數在內的全部聲明都會在任何代碼被執行前首先被處理。--先編譯,在執行。
console.log(a); //undefinde var a = 2; console.log(a); //2
這也解釋了爲何第一行沒有報錯的緣由。
首先代碼執行前一刻進行編譯,var a; console.log(a); a = 2; console.log(a);相似這樣的執行順序(就好像變量和函數從他們的代碼中出現的位置被移動到了最上面)。 編譯器看到var a會查看當前做用域是否有變量a,沒有聲明一個變量a,開始執行代碼(js是順序執行)。
當函數和變量同名時,函數會覆蓋變量。
由此預編譯過程能夠總結成一下過程
預編譯四部曲: