JavaScript變量提高

問題

有些朋友可能會以爲javascript的代碼是從上到下,一行一行的解釋執行的。若是按照這樣的思路,在有些狀況下閱讀代碼會獲得錯誤的結果,考慮如下代碼:javascript

a = 2;
var a;
console.log(a);

console.log(a)應該輸出什麼呢?有些開發者以爲會輸出undefined,由於var a在'a = 2'以後,變量a被重複定義了,可是沒有被賦值,因此是'undefined'。可是結果輸出是2。以下圖所示:java

圖片描述

咱們再來考慮另外一段代碼,以下所示:函數

console.log(a);
var a = 2;

這段代碼會輸出什麼樣的結果呢?有些人可能會以爲輸出ReferenceError。由於變量a在沒有聲明的狀況下就被使用了。真實結果呢,以下圖所示:輸出的是undefined優化

圖片描述

爲何會這樣呢?這就牽出了本文的主題:JavaScript聲明提高spa

JavaScript代碼的運行規則

在JavaScript代碼運行以前實際上是有一個編譯階段的。編譯以後纔是從上到下,一行一行解釋執行。變量提高就發生在編譯階段,它把變量和函數的聲明提高至做用域的頂端。(編譯階段的工做之一就是將變量與其做用域進行關聯)。
因此對於代碼var a =2;來講,編譯器看到的是兩行代碼var a; a = 2;第一個語句是聲明語句,在編譯階段處理。第二個語句是賦值語句,在運行階段處理。
那麼咱們再回過頭來看看問題中出現的代碼:3d

a = 2;
var a;
console.log(a);

應該這樣來處理:code

var a;            //編譯階段
a = 2;            //運行階段
console.log(a);   //運行階段

因此這段代碼的最終輸出的結果是2blog

第二段代碼:圖片

console.log(a);
var a = 2;

應該這樣來處理:ip

var a;            //編譯階段
   console.log(a);   //運行階段 
   a = 2;            //運行階段

因此這段代碼的最終輸出結果是undefined

變量提高須要注意兩點:

  1. 提高的部分只是變量聲明,賦值語句和可執行的代碼邏輯還保持在原地不動

  2. 提高只是將變量聲明提高到變量所在的變量範圍的頂端,並非提高到全局範圍,說明以下:

foo();
function foo(){
    console.log(a); //會輸出undefined
    var a = "2";
}
//變量提高以後的效果
function foo(){
    var a;
    console.log(a);
    a = "2";
}
foo();

函數聲明會提高,可是函數表達式就不了。看以下代碼:

foo();
var foo = function bar(){    //這是一個函數表達式,再也不是函數聲明。
    console.log("bar");
}

處理方式以下:

var foo;    
foo();    //TypeError,由於尚未賦值
bar();    //bar不能夠在全局範圍內引用
foo = function bar(){            
    console.log("bar");
}

函數是一等公民

變量聲明和函數聲明都會獲得變量提高,但函數聲明會最早獲得提高,而後是變量聲明。
考慮以下代碼:

foo();    //輸出的結果爲1
var foo;
function foo(){
    console.log(1);
}
foo = function(){
    console.log(2);
}

處理方式以下:

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

注意var foo;因爲是重複聲明變量,因此被編譯優化去掉。

最後再來講明幾種狀況

對於函數聲明來講,若是定義了相同的函數變量聲明,後定義的聲明會覆蓋掉先前的聲明,看以下代碼:

foo();    //輸出3
function foo(){
    console.log(1);
}
var foo = function(){
    console.log(2);
}  
function foo(){
    console.log(3);
}

JavaScript中是沒有塊級做用域的概念(ps:ES6中有改進了),看以下代碼:

foo();    //輸出結果爲2
var a = true;
if(a){
    function foo(){
        console.log(1);
    }
}else{
    function foo(){
        console.log(2);
    }
}

這段代碼輸出結果爲2,if語句沒有塊級做用域的功能,因此函數聲明都被提高到全局做用域中,又由於定義了兩個foo,後來的定義覆蓋了前邊的定義,因此輸出結果爲2。

相關文章
相關標籤/搜索