JS變數聲明提升
首先來看一段程式碼
console.log(a) var a = 2;
輸出結果是2,正常來說JS如果是逐行向下執行,那麼應該輸出undefined,為何此處輸出2呢?
原因在於JS在執行前都會進行編譯(通常就在執行前),在編譯過程中包括變數和函數在內的所有聲明都會被處理。
定義
是指在 JavaScript 程式碼執行前的編譯過程中,JavaScript 引擎把變數的聲明部分和函數的聲明部分提升到程式碼開頭的「行為」。變數被提升後,會給變數設置默認undefined。
那let,const是否會進行變數聲明提升呢?
首先我們要知道定義一個JS變數分為三個階段
- 創建create
- 初始化initialize
- 賦值assign
下面我們分別來看看var、let、function 和 const的過程
var聲明
function fn() { var x = 1; } fn();
- 進入 fn,為 fn 創建一個環境。
- 找到 fn 中所有用 var 聲明的變數,在這個環境中「創建」這些變數(即 x 和 y)。
- 將這些變數「初始化」為 undefined。
- 開始執行程式碼
- x = 1 將 x 變數「賦值」為 1
由以上步驟可知,var 聲明會在程式碼執行之前就將創建變數,並將其初始化為 undefined。即創建和初始化會被提升
function聲明
fn(); function fn() { console.log(1); }
- 找到所有用 function 聲明的變數,在環境中「創建」這些變數。
- 將這些變數「初始化」並「賦值」為 function(){ console.log(1) }。
- 開始執行程式碼 fn2()
由以上步驟可知,function 的「創建」「初始化」和「賦值」都被提升了
let聲明
{ let x = 1; x = 2; }
- 找到所有用 let 聲明的變數,在環境中創建這些變數
- 開始執行程式碼(注意現在還沒有初始化)
- 執行 x = 1,將 x 初始化為 1(這並不是一次賦值,如果程式碼是 let x,就將 x 初始化為 undefined)
- 執行 x = 2,對 x 進行賦值
由以上步驟可知,let只有創建過程會提升,初始化和賦值都不會提升,所以會形成暫時性死區,這也是為什麼在定義前使用let會拋錯。
暫時性死區,就是不能在初始化之前,使用變數
const聲明
{ console.log(x); const x = 1; }
- 找到所有用 const聲明的變數,在環境中「創建」這些變數
- 開始執行程式碼(注意現在還沒有初始化)
- 執行x = 1, 將x初始化為1
由以上步驟可知,const的創建過程也會提升,但是與let不同之處在於const只有創建和初始化兩個過程,沒有賦值過程。若不初始化則會直接拋錯
總結
- var 的創建和初始化都被提升了。
- function 的創建、初始化和賦值都被提升了。
- let 的創建被提升了,但是初始化和賦值沒有提升。
- const的創建被提升但是初始化沒提升。const沒有賦值。
注意⚠️
函數優先
何為函數優先,來看一下下面的程式碼👇
foo(); var foo; function foo() { console.log(1) }
實際上輸出值會是1,因為當變數聲明和函數聲明同時存在時,函數聲明優先於變數聲明,即函數聲明會覆蓋變數聲明。原因在於函數的賦值過程也會提升。那有人可能有疑問,如果將var變成let呢?結果為foo不允許被重複聲明。
變數提升的存儲位置
執行上下文中存在一個變數環境的對象(Viriable Environment),該對象中保存了變數提升的內容。而let,const則存在詞法環境中。
瀏覽器原理與實踐