變量提升:變量的聲明寫在可以在使用變量之後;
函數提升:函數可以先調用,後聲明;
上面先解釋了下我理解的這兩個概念的定義。要真正理解它們,最好從變量對象的角度出發。引出變量對象的概念,要先理解執行上下文,也就是當控制器執行到可執行代碼的時候,就會生成一個執行上下文,也就是代碼的執行環境,會產生一個作用域。這個執行上下文的生命週期有兩個階段:創建階段 和 代碼執行階段
創建階段就會創建變量對象,創建的過程分三步:
1.建立arguments對象。檢查當前上下文中的參數,建立該對象下的屬性與屬性值
2.先檢查有沒有函數聲明(利用function關鍵字聲明的函數,在變量對象中用函數名作爲屬性名,屬性值是函數內存地址
的引用。如果函數名的引用已經存在,那麼會被新的引用覆蓋)
3.再檢查變量聲明,每找到一個變量聲明,就在變量對象裏用變量名作爲屬性名,屬性值是undefined。如果變量名已經存在,直接跳過,目的是防止把同名的函數被修改成undefined
執行階段會將變量對象轉換爲活動對象:
創建完成之後,就會開始執行代碼,這個時候,會完成變量賦值,函數引用,以及執行其他代碼。
(在變量對象創建過程中,其中的屬性不可訪問。只有在執行階段轉爲活動對象後,纔可以訪問其中的內容)
先來一個簡單的例子:
function foo(){
a = 2;
function a(){
console.log("函數")
}
console.log(a); //2
var a;
};
foo();
這個例子中,當控制器遇到這段代碼時候,會形成一個執行上下文。隨後進入到執行上下文的第一個生命週期:創建階段,此時,執行創建變量對象的三個步驟。
- 由於沒有參數。直接進入第二步。
- 首先檢查上下文中有沒有function關鍵字聲明的函數,以a爲屬性名,函數的引用爲屬性值,添加到變量對象中。
- 再檢查函數聲明,發現了變量名a,但是,以a爲屬性名的屬性值已經存在,所以跳過。
接下來進入第二個生命週期:代碼執行
a被賦值爲2,函數a未被調用。打印a等於2
函數提升也是一個道理,由於變量對象創建過程中會先檢查function關鍵字聲明的函數,以該函數的函數名爲屬性,值爲函數地址的引用,添加進變量對象中。