當瀏覽器中的GUI線程解析html過程當中發現js代碼後,會告知Browser進程下載js文件。js文件下載完成後,js主引擎解析並執行js。因爲js主引擎和GUI線程互斥,因此在js執行過程當中,GUI線程掛起,html解析暫停,直至js代碼執行完畢。html
js主引擎解析並執行js可分爲三個步驟:瀏覽器
因爲js是腳本語言,所以預編譯階段和執行階段是幾乎同時進行的,即編譯一段代碼就執行一段代碼。閉包
js代碼在執行以前會生成執行環境(EC),也能夠稱爲執行上下文。函數
每當程序的執行流進入到一個可執行的代碼時,就進入到了一個執行環境中。
執行上下文包含三種:this
生成執行環境時生成執行上下文對象,執行上下文對象以下線程
fn.ExecutionContext = { variableObject: // 函數中的 arguments、參數、局部成員 scopeChains: // 當前函數所在的父級做用域中的活動對象 this: {} // 當前函數內部的 this 指向 }
在執行上下文對象中包含variableObject屬性,也稱爲變量對象(VO)。此時,VO中的變量只是聲明,並無賦值。code
當進入執行階段後,執行上下文會部分發生變化,以下:htm
fn.ExecutionContext = { activationObject: // 函數中的 arguments、參數、局部成員 scopeChains: // 當前函數所在的父級做用域中的活動對象 this: {} // 當前函數內部的 this 指向 }
此時,執行以前的變量對象(VO)變成了活動對象(AO),也就是完成了變量的賦值。對象
做用域指明瞭一部分自由變量起做用的範圍,目前js包含三種,即:進程
函數在聲明的時候會肯定函數做用域,多個有關係的做用域合在一塊兒能夠造成做用域鏈。
function main() { // main函數內部的做用域鏈保存着全局做用域 return function inner() { // inner內部的做用域鏈首先保存着main函數的做用域,還保存着全局做用域 } }
經過做用域鏈能夠完成變量值的查找,若是inner函數執行的時候須要a變量,inner函數首先查找本身的做用域,看是否認義了a變量,若是沒有,則向上查找main函數的做用域,若是沒有,則查找全局做用域,當整個查找鏈條都沒有找到a變量,則會提示a變量undefined,反之,若是在某個做用域內遭到變量a,那麼就中止查找。
做用域鏈要求只能從內向外查找,不能從外向內查找,也就是main函數查找變量時,只能查找自己做用域和全局做用域,而不能查找inner函數做用域。
使用閉包能夠突破變量做用域的限制,原來只能從一個做用域訪問外部做用域的成員,有了閉包以後,能夠在外部做用域訪問一個內部做用域的成員。