前言
看了方應杭老師的一篇解釋let的文章,對JavaScript中的聲明有了深刻的理解,這里也就有了總結一下JavaScript中各種聲明之間區別的這篇文章。
JavaScript中變量聲明機制
首先,我對JavaScript中所有聲明都存在提升
這個觀點是認同的!
平時大家所講的變量的聲明
,在JavaScript中一般是存在創建create
、初始化initialize
和賦值assign
三個過程的,其中函數聲明也是一樣有這三個過程的。在這三個過程中:
創建:即在變量所在作用於頭部拋出變量,僅僅是拋出,是不能被使用的;
初始化:在變量初始化之前變量是不能被使用的,初始化只有一次,初始化之后變量可以使用;
賦值:即覆蓋初始化的值
在let、var、const(包括函數聲明)的聲明過程中,他們的創建create
、初始化initialize
和賦值assign
是有區別的。
let
let是ECMAScript2015(ES6)中新增的變量聲明方式,let語句可以聲明一個塊級作用域的變量。
在同一個函數或同一個作用域中用let重復定義一個變量將引起 TypeError
if (true) {
let foo;
let foo; // TypeError thrown.
}
在let語句中,創建create
、初始化initialize
和賦值assign
的過程如下:
if (true) {
let foo
console.log(foo) //undefined
foo = 'str'
console.log(foo) //"str"
}
通過上面代碼可以看出let語句是如下過程的:
第一步:在塊級作用域中,找到let語句並聲明提升,創建foo變量,但是在初始化之前使用是會報錯的(如下面代碼);
第二步:執行let foo
,let語句使得foo變量被初始化,初始化的過程是可選的,可以let foo
初始化foo為undefined,也可以let foo = 123
初始化foo為123.初始化之后變量可以使用;
第三步:執行foo = 'str'
,foo變量可以被賦值語句覆蓋
if (true) {
console.log(foo) //Uncaught ReferenceError: foo is not defined
let foo;
}
所以說,let聲明中在初始化之前是存在“暫時性死區”的。
還有值得注意的是,初始化只能一次,如果變量初始化失敗,則會存在一個該變量既不能賦值又不能使用的BUG,如下圖:
因為let foo = foo
導致初始化失敗,所以導致了foo所在作用域都是foo變量只創建而未被初始化,所以foo所在作用域都會是foo變量的“暫時性死區”
var
var聲明是函數作用域。
眾所周知,var聲明是有變量提升的,有了上文的let聲明的理解,也就對var理解起來更加輕松了。
(function() {
console.log(f) //undefined
var f = 'str' //
console.log(f) //"str"
}())
var聲明的過程:
第一步:在函數作用域中,找到var語句,f變量得到聲明提升到作用域頂部,創建f變量並初始化為undefined,在賦值之前f的值就是undefined;
第二步:執行f = 'str'
,f變量被賦值為'str'
所以,在執行var f = 'str'
之前,打印的f的值為undefined
const
const聲明創建了一個常量
const和let比較像,也是塊級作用域,區別就是,const在定義的時候必須初始化,而且不能被賦值
如上圖,const在定義時必須初始化,否則會報錯Uncaught SyntaxError: Missing initializer in const declaration
;const是常量,聲明之后不能再次賦值,否則會報錯Uncaught TypeError: Assignment to constant variable
函數聲明
函數聲明在JavaScript存在變量提升,這也被眾多開發者做開發所常用。
fn()
function fn(){
console.log(this)
}
函數聲明的過程:
第一步:在作用域中,找到function語句,將fn函數聲明提升到作用域頂部,創建fn函數,初始化並賦值為
function fn(){console.log(this)}
;
第二步:執行fn()
即函數聲明是在找到function語句后,作用域頂部創建、初始化和賦值一步到位的
總結
在MDN關於let講解中,說let沒有聲明,也許是考慮到更容易被開發者接受和理解吧,但是我認為JavaScript中所有聲明都存在提升
是正確的。
最后用一個表格總結一下let、var、const聲明的區別吧
--- | let | var | const |
---|---|---|---|
變量or常量 | 變量 | 變量 | 常量 |
作用域 | 塊級作用域 | 函數作用域 | 塊級作用域 |
創建 | 作用域頂部創建 | 作用域頂部創建 | 作用域頂部創建 |
初始化 | let語句 | 作用域頂部 | const語句 |
賦值 | 可以賦值 | 可以賦值 | 報錯 |
重復聲明 | 報錯 | 可以重復聲明 | 報錯 |
暫時性死區 | 有 | 沒有 | 有 |
本文首發在個人博客yoowin.me,歡迎訪問。