執行上下文就是JavaScript 在被解析和運行時環境的抽象概念,JavaScript 運行任何代碼都是在執行上下文環境中運行的,執行上下文包括三個周期:創建——運行——銷毀,重點說一下創建環節。
創建環節(函數被調用,但未未被執行)會執行三件事情
- 創建變量對象,首先初始化函數的arguments對象,提升函數聲明和變量聲明,從近到遠查找函數運行所需要的變量。
- 創建作用域鏈,作用域就是一個獨立的地盤,讓變量不會相互干擾,當前作用域沒有定義的變量,這成為 自由變量。自由變量會向上一直尋找,要到創建這個函數的那個作用域中取值——是“創建”,而不是“調用”,如果最終沒有就為undefined。這種層層之間就構成了作用域鏈。
- 確定this指向,this、apply、call的指向
function test(arg){ // 1. 形參 arg 是 "hi" // 2. 因為函數聲明比變量聲明優先級高,所以此時 arg 是 function console.log(arg); var arg = 'hello'; // 3.var arg 變量聲明被覆蓋, arg = 'word'被執行 function arg(){ console.log('hello world') } var arg = 'word'; console.log(arg); } test('hi');
可以看下上面這個例子,函數內函數聲明比變量聲明優先,所以arg被覆蓋,並且被提升,所以第一次打印不會報錯,打出了arg函數,后面變量被覆蓋成為hello。
函數執行多了就會有多個執行上下文,那么怎么管理這些執行上下文呢?
JavaScript 引擎創建了執行棧來管理執行上下文,可以把執行棧認為成一個儲存函數調用的棧結構,遵循先進后出的原則。
從上面代碼執行,我們大概可以得出以下幾點
- JavaScript引擎是單線程執行,所有代碼都是排隊執行。
- 一開始執行的是全局代碼,首先創建全局的執行上下文,然后將該執行上下文壓入執行棧中。
- 每當執行一個函數,就會創建該函數的執行上下文,然后將其壓入執行棧的頂部,函數執行完成后,執行上下文從底部退出,等待垃圾回收。
- 游覽器js總是訪問執行棧頂層的執行上下文。
- 全局上下文只有唯一的一個,它在瀏覽器關閉時出棧