在很多面試題中,經常會看到關于變量提升,還有函數提升的題目,所以我就寫一篇自己理解之后的隨筆,方便之后的查閱和復習。
首先舉個例子
foo();//undefined
function foo(){
console.log(a);//undefined
var a = 10;
}
上面的例子中,foo()
函數的聲明在調用之后,但是還是會輸出函數中的結果。在foo()
函數的內部,變量a
的聲明之前就調用了,但是系統會輸出undefined
,而不會報錯,這里面就涉及到了變量提升
還有函數提升
,為什么會出現這樣的情況呢,先來了解幾個重要的概念,就可以解開這個謎底了。
一、什么是執行上下文
每次當控制器轉到可執行代碼的時候,就會進入一個可執行上下文。執行上下文可以理解為當前代碼的執行環境,它會形成一個作用域。
一個執行上下文的生命周期
執行上下文.PNG
從圖中可以看出,一個執行上下文的生命周期包括
創建
和執行
兩個階段。1、第一個階段中做的工作有:
- 生成變量對象
- 建立作用域鏈
- 確定this的指向
2、第二個階段中做的工作有:
- 變量賦值
- 函數引用
- 執行其他代碼
到這里還不能解析為什么會有變量提升和函數提升的過程,那么我們接下來再深入執行上下文
中的創建
階段中的生成變量對象的過程
什么是變量對象
在很多面試題中,會考到變量對象和活動對象的區別,其實活動對象和變量對象是同一個對象,只是處于執行上下文的不同生命周期而已,活動對象處于執行階段。
變量對象.PNG
從圖中可以看出,在生成變量對象的過程中,執行的操作有三步
1、創建arguments對象
2、檢查functfen函數聲明創建屬性
3、檢查var變量聲明創建屬性
所以在了解完執行上下文和變量對象后,就可以分析一下我們的問題了。
當我們執行第一句話的時候foo()
,javascript引擎會創建一個執行上下文testEc
,并且生成一個變量對象VO
,首先創建argument對象
,然后檢查function
函數聲明并且創建一個屬性,所以就生成了如下的vo對象
創建過程
testEC = {
// 變量對象
VO: {},
scopeChain: {},
this: {}
}
// 因為本文暫時不詳細解釋作用域鏈和this,所以把變量對象專門提出來說明
// VO 為 Variable Object的縮寫,即變量對象
VO = {
arguments: {...}, //注:在瀏覽器的展示中,函數的參數可能并不是放在arguments對象中,這里為了方便理解,我做了這樣的處理
foo: <foo reference> // 表示foo的地址引用
a: undefined
}