變數提升和函式提升的總結
我們在學習JavaScript時,會遇到變數提升和函式提升的問題,為了理清這個問題,現做總結如下,希望對初學者能有所幫助
我們都知道 var 宣告的變數有變數提升,而 let 宣告的變數和 const 宣告的常量沒有變數提升,但是為什麼 let 宣告的變數就不會變數提升呢?
首先我們得理解變數和函式的建立過程:
變數宣告 ==>變數初始化 ==>變數賦值
函式宣告 ==> 初始化 ==> 賦值
而變數提升和函式提升要完成的工作為:
變數宣告 ==> 變數初識化(undefined)
函式宣告 ==> 初始化 ==> 賦值(僅僅是賦值,並沒有執行函式體)
暫時性死區的概念:
ES6規定,如果程式碼區塊中存在 let 和 const 命令宣告的變數,這個區塊對這些變數從一開始就形成了封閉作用域,直到宣告語句完成,這些變數才能被訪問(獲取或設定),否則就會報錯 ReferenceError。這在語法上稱為“暫時性死區”,即程式碼塊開始到變數宣告語句完成之間的區域。通過 var 宣告的變數擁有變數提升,沒有暫時性死區。
因為有暫時性死區的存在,所以 let 宣告的變數和 const 宣告的常量才 不能 變數提升
從變數建立過程來理解:
let 宣告被提升了,但是並沒有做初始化工作
const 宣告被提升,也是沒有做初始化工作(const只有宣告和初始化過程,沒有賦值過程)
let 和 const 都沒有提升所要滿足的工作,所以就無法提升
var 的宣告被提升,初始化被提升
function 的宣告、初始化、和賦值都被提升
var 宣告的變數提升(宣告、初始化都被提升,但是賦值沒有提升)
例項
console.log(a) // undefined
var a = 'hello world'
console.log(a) // 'hello world'
實際執行過程如下
var a
console.log(a) // undefined
a = 'hello world'
console.log(a) // 'hello world'
function 宣告的函式提升(宣告、初始化、賦值都提升了)
例項
fn() // 'hello world'
function fn() { console.log('hello world') }
fn() // 'hello world'
實際執行過程如下
function fn() { console.log('hello world') } // 注意:此時函式體並沒有執行
fn() // 'hello world'
fn() // 'hello world'
既有 var 宣告的變數提升,又有 function 宣告的函式提升
例項
變數名和函式名不同名
console.log(a) // undefined
fn()
var a = 100
console.log(a) // 100
function fn() {
console.log(a) // undefined
var a = 10
console.log(a) // 10
}
var a = 200
console.log(a) // 200
實際執行過程如下
function fn() {
console.log(a)
var a = 10
console.log(a)
}
var a (全域性變數)
var a (區域性變數)
console.log(a) // undefined
fn() // undefined 10
a = 100
console.log(a) // 100
a = 200
console.log(a) // 200
變數名和函式名同名
情況一:變數由 函式型別 變為 基本資料型別(如 Number)
var a = 100
console.log(a) // 100
a() // 報錯(TypeError:a is not a function)
function a() {
console.log('hello world')
}
實際執行過程如下
function a() {
console.log('hello world')
}
var a // 用 var 重新聲明瞭變數 a,但此時變數 a 指向的還是函式體本身,值未改變
a = 100 // 重新給 a 賦值為 100,a 的值發生了改變,由函式型別變為了number型別
console.log(a) // 100
a() // 報錯:此時 a 已經不是函式 a 了
情況二:變數重新賦值,但未立即改變變數的值
console.log(a) // a(){ console.log('hello world')} 即 a 函式本身
a() // 'hello world'
var a = 100
var a = 200
var a = 300
console.log(a) // 300
function a() {
console.log('hello world')
}
實際執行過程如下
function a() {
console.log('hello world')
}
var a // 此時雖然重新聲明瞭 a 變數,但是並沒有改變 a 的值,此時的 a 還是個函式
console.log(a) // a(){ console.log('hello world')} 即 a 函式本身
a = 100 // 重新給 a 變數賦值 100
a = 200 // 重新給 a 變數賦值 200
a = 300 // 重新給 a 變數賦值 300
console.log(a) // 100(此時變數 a 已經由 函式型別變為 基本資料型別number,且值為 100)
a() // 報錯:此時 a 已經不是函式 a 了
用 let 定義的變數和用 const 定義的常量 沒有提升
用 let 定義的變數
在變數定義之前使用改變數 會報錯
console.log(a) // 報錯 Cannot access 'a' before initialization
let a = 10
在變數定義之後使用改變數 才能正常執行
let a = 10
console.log(a) // 10
總結:
1. 用 var 定義的變數有變數提升,用 function 定義的函式有函式提升
2. 函式提升優先順序高於變數提升,即優先執行函式提升
3. let 定義的變數和 const 定義的常量不會提升
4. 重複用 var 定義的同一個變數是合法的,如果重複的宣告有初始值,那就賦值;如果沒有初始值,那麼它不會對變數原來儲存的值不會產生影響
5. 不能用 let 重複定義同一個變數,會造成錯誤