變量聲明提升和函數聲明提升

變量聲明提升
1、變量定義
可以使用var定義變量,變量如果沒有賦值,那變量的初始值爲undefined。

2、變量作用域
變量作用域指變量起作用的範圍。變量分爲全局變量和局部變量。全局變量在全局都擁有定義;而局部變量只能在函數內有效。
在函數體內,同名的局部變量或者參數的優先級會高於全局變量。也就是說,如果函數內存在和全局變量同名的局部變量或者參數,那麼全局變量將會被局部變量覆蓋。
所有不使用var定義的變量都視爲全局變量

3、函數作用域和聲明提前
JavaScript的函數作用是指在函數內聲明的所有變量在函數體內始終是有定義的,也就是說變量在聲明之前已經可用,所有這特性稱爲聲明提前(hoisting),即JavaScript函數裏的所有聲明(只是聲明,但不涉及賦值)都被提前到函數體的頂部,而變量賦值操作留在原來的位置。如下面例子:
註釋:聲明提前是在JavaScript引擎的預編譯時進行,是在代碼開始運行之前。

var scope = 'global';
function f(){
    console.log(scope);
    var scope = 'local';
    console.log(scope);
}

由於函數內聲明提升,所以上面的代碼實際上是這樣的

var scope = 'global';
function f(){
    var scope;    //變量聲明提升到函數頂部
    console.log(scope);
    scope = 'local';    //變量初始化依然保留在原來的位置
    console.log(scope);
}

經過這樣變形之後,答案就就非常明顯了。由於scope在第一個console.log(scope)語句之前就已經定義了,但是並沒有賦值,因此此時scope的指是undefined.第二個console.log(scope)語句之前,scope已經完成賦值爲’local’,所以輸出的結果是local。

函數聲明提升
函數聲明語法

f('superman');
function f(name){
    console.log(name);
}

函數表達式語法

f('superman');
var f= function(name){
    console.log(name);
}

運行上面的代碼,會報錯Uncaught ReferenceError: f is not defined(…),錯誤信息顯示說f沒有被定義。
爲什麼同樣的代碼,函數聲明和函數表達式存在着差異呢?
這是因爲,函數聲明有一個非常重要的特徵:函數聲明提升,函數聲明語句將會被提升到外部腳本或者外部函數作用域的頂部(是不是跟變量提升非常類似)。正是因爲這個特徵,所以可以把函數聲明放在調用它的語句後面。如下面例子,最終的輸出結果應該是什麼?:

var getName = function(){
    console.log(2);
}
function getName (){
    console.log(1);
}
getName();

可能會有人覺得最後輸出的結果是1。讓我們來分析一下,這個例子涉及到了變量聲明提升和函數聲明提升。正如前面說到的函數聲明提升,函數聲明function getName(){}的聲明會被提前到頂部。而函數表達式var getName = function(){}則表現出變量聲明提升。因此在這種情況下,getName也是一個變量,因此這個變量的聲明也將提升到底部,而變量的賦值依然保留在原來的位置。需要注意的是,函數優先,雖然函數聲明和變量聲明都會被提升,但是函數會首先被提升,然後纔是變量。因此上面的函數可以轉換成下面的樣子:

function getName(){    //函數聲明提升到頂部
    console.log(1);
}
var getName;    //變量聲明提升
getName = function(){    //變量賦值依然保留在原來的位置
    console.log(2);
}
getName();    // 最終輸出:2

所以最終的輸出結果是:2。在原來的例子中,函數聲明雖然是在函數表達式後面,但由於函數聲明提升到頂部,因此後面getName又被函數表達式的賦值操作給覆蓋了,所以輸出2。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章