1. 程式人生 > >JavaScript中的變數提升與函式提升

JavaScript中的變數提升與函式提升

我們先來看下面這一段程式碼

console.log(foo)  //undefined
var foo = ”test“

很奇怪對吧,分明foo字串宣告在輸出語句之後,為什麼輸出的結果只是“未找到值”而不是“未找到該變數”呢?別急,我們看接下來的這個例子。

var x = new Date()

function foo (){
    console.log(x)  //undefined
    if(0){
        var x = 10  
    }
    console.log(x) //undefined
}

foo()
console.log(x) //2017-10-13T11:53:30.772Z

是不是覺得更奇怪了?不應該是輸出時間資訊麼?怎麼還是找不到值?

這裡首先要引入作用域的概念。在JavaScript中是沒有塊級作用域這個概念的,就是一對花括號“{}”括起來的區域,只有全域性作用域和函式作用域。然後我們在引入變數提升的的定義了,“函式宣告和變數宣告總是被JavaScript直譯器隱式地提升(hoist)到包含他們的作用域的最頂端。”

什麼意思呢?我們按這段話的意思來改寫一下上面那段程式碼,看看它的真正面目。

var x
x = new Date()

function foo(){
    var x
    console.log(x)  //undefined
    if(0){
        x = 10
    }
    console.log(x)  //undefined
}

foo()
console.log(x)  //2017-10-13T11:53:30.772Z

由於沒有塊級作用域只有函式作用域,所以無論變數被宣告在函式內的什麼地方,變數的宣告都會被提升到作用域的最頂端,但是請注意僅僅是宣告提前了,並沒有賦值。所以在這個例子中,if語句內宣告的變數x被提前到函式頂部宣告,這樣就覆蓋了全域性作用域中的x變數,但由於沒有賦值,所以第一個輸出語句的值只能是"undefined",又由於“if(0)”,所以if語句內的程式碼不會被執行,也就是說x變數不會被賦值,所以第二個輸出語句依舊輸出“undefined”。而第三個輸出語句則是正常輸出了全域性作用域中的x變數。

同理,那麼我給的第一段程式碼中出現的情況就好理解了。就是先宣告卻沒賦值,所以輸出語句找不到foo變數的值就只能輸出“undefined”了。


其實,不僅僅是變數。函式也一樣有提升的情況,剛剛給出定義的時候也提到了。我們來看一段程式碼。

console.log(f)  //[Function: f]
console.log(x)  //undefined

function f(){
    console.log("Hello")
}

var x = function(){
    console.log("world")
}

要注意JavaScript中函式是一等公民,所以函式永遠先被提升,然後才是變數。跟上面的一樣,x變數先被提升到全域性域頂部宣告,此時並沒有賦值(它的值是個匿名函式)。其實函式提升的初衷是為了,函式之間的相互遞迴,不然的話按順序執行,總會有一個函數出現未宣告的情況。

注:如果在同一個作用域中存在多個同名函式宣告,後面出現的將會覆蓋前面的函式宣告。


在ES6中引入了let,const關鍵字,使用這兩個關鍵字宣告的變數是不會上面出現的變數提升的情況的,但是....

'use strict'

var i = 'test'

function foo(){
	console.log(i)
	let i = 10
}

foo() // i is not defined

事實上,使用let宣告的變數還是提升了的,不然的話,就應該直接輸出“test”。ES6中規定,如果區塊中存在let,const關鍵字,這個區塊對這些命令宣告的變數會從一開始形成一個封閉作用域,只要在宣告之前使用該變數就會報錯。