var, let, const
這三個東西, 經常會被問到。
彙總一下,基本上就是:
var, let, const
有什麼區別
?- let, const 有沒有
變量提升
(hosting) ? - 什麼是
TDZ
?
首先, 我們先整體的看下區別
:
針對這幾點, 我們一個個看。
首先, 我們還是先了解一下變量提升
.
看個例子:
console.log(a) // undefined
var a = 1
這裏我們可以看到, 第一行中的a雖然還沒聲明, 但是我們用起來卻不會報錯。 這種情況, 就是聲明的提升。
其實也就是:
var a
console.log(a) // undefined
a = 1
但是, 如果是換成let
, 情況就不一樣了:
要理解這個現象, 首先需要搞清楚提升的本質, 理解創建 javascript 變量的三個步驟
:
創建
初始化
賦值
爲了便於理解, 我們先看看var
的 創建、初始化和賦值
過程:
function foo(){
var x = 1
var y = 2
}
foo()
執行foo 時, 會有一些過程(部分過程):
- 進入 foo,爲 foo 創建一個環境。
- 找到 foo 中所有用 var 聲明的變量,在這個環境中「創建」這些變量(即 x 和 y)。
- 將這些變量「初始化」爲 undefined。
- 執行代碼
- x = 1 將 x 變量「賦值」爲 1
- y = 2 將 y 變量「賦值」爲 2
也就是說 va
聲明, 會在代碼執行之前就將 創建變量
,並將其初始化爲 undefined
。
這就解釋了爲什麼在 var x = 1 之前 console.log(x) 會得到 undefined
。
接下來看 let 聲明的「創建、初始化和賦值」過程
// ...
{
let x = 1;
x = 2
}
我們看一下過程:
- 找到所有用
let
聲明的變量,在環境中創建
這些變量 - 執行代碼(注意現在還沒有初始化)
- 執行 x = 1,將 x 「初始化」爲 1(這並不是一次賦值,如果代碼是 let x,就將 x 初始化爲 undefined)
- 執行 x = 2,對 x 進行「賦值」
這就解釋了爲什麼在 let x 之前使用 x 會報錯:
let x = 'global'
{
console.log(x) // Uncaught ReferenceError: x is not defined
let x = 1
}
原因有兩個
- console.log(x) 中的 x 指的是下面的 x,而不是全局的 x.
- 執行 log 時 x 還沒「初始化」,所以不能使用(也就是所謂的
TDZ
, tempory dead zone, 暫時死區)
說到這裏, 就都清楚了:
- let 的「創建」過程被提升了,但是初始化沒有提升。
- var 的「創建」和「初始化」都被提升了。
function 也是類似的,而且function 的「創建」「初始化」和「賦值」都被提升了。
這一點, 感興趣的朋友可以自己實驗一下。
算了, 直接給個例子吧:
<script>
bar()
function bar(){
console.log(2)
}
</script>
JS 引擎會有一下過程:
- 找到所有用 function 聲明的變量,在環境中「創建」這些變量。
- 將這些變量「初始化」並「賦值」爲
function(){ console.log(2) }
。 - 開始執行代碼 fn2()`
也就是說 function 聲明會在代碼執行之前就「創建、初始化並賦值」。
這裏做一下簡單的總結:
函數提升優先於變量提升
.函數提升
會把整個函數挪到作用域頂部
,變量提升
只會把聲明
挪到作用域頂部
。var
存在提升
,我們能在聲明之前
使用。let
,const
因爲存在暫時性死區
,不能在聲明前使用
。var
在全局作用域
下聲明變量, 會導致變量掛載在window
上, 而另外兩者不會
。let
和const
的作用基本一致,但是後者聲明的變量不能再次賦值。