JavaScript的變量提高機制

變量提高

JavaScript的變量提高有兩種,用var聲明的變量以及用function聲明的變量。javascript

用var聲明的變量

咱們先來看下面這段代碼,a的值是多少java

代碼1編程

console.log(a);

var a;

按照以往編程語言的思路來看,代碼自上而下運行,按這種思路,會報錯,由於執行到第2行時,變量a尚未定義,因此會報錯a is not defined編程語言

然而事實上答案是undefined函數

好,抱着疑惑,咱們看下面的代碼code

var a;
console.log(a);

咱們發現,這兩段代碼是同樣的,那麼又有一個新的問題,是否是有沒有var a都無所謂,它的答案始終是undefined,才形成了覺得變量會提高的錯覺,因而我寫了代碼3ip

代碼3作用域

console.log(a);

好,它終於報錯了,因此這證實了javaScript代碼並非自上而下執行的,至少從表面看上面是這樣的。開發

因而咱們再看代碼4編譯器

代碼4

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

由於變量提高嘛,因此答案是2,然而事實上,它依然是undefined,why?

這時候咱們有請編譯器這位負責語法分析及代碼生成等髒活累活的大佬。

編譯器在看到var a = 2;,它會將其看作兩個聲明,var a;a = 2,第一個聲明在編譯階段進行,第二個聲明會被原地等待執行階段。

也就是說上面代碼,會變成下面的這段代碼

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

因此最終會是undefined

好,我在囉嗦一下,看這段代碼5

代碼5

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

我想你們應該已經知道這段代碼執行時的真正順序及其答案了,沒錯,答案是2,但我想說的是把第2行給註釋掉,答案依然是2,但這個和變量提高沒啥關係了,是嚴格模式與非嚴格模式的鍋,在非嚴格模式下容許開發者能夠不使用聲明變量的關鍵字,但在嚴格模式下是不能夠的,它會報錯的。

用function聲明的變量

var同樣,function聲明的變量依然會提高。

log(5);

function log(mes){
    console.log(mes)
}

按照以前的變量提高的理解,這段代碼的真正順序是這樣的,

function log(mes){
    console.log(mes)
}

log(5);

很好,很正確,那麼再看下一段代碼

log(5);

var log = function(mes){
    console.log(mes)
}

它報錯了,log is not a function,從這裏咱們能夠看出,這種函數表達式是不會被提高的,只有函數聲明纔會被提高,試着在最前面新增一行代碼console.log(log),會先輸出undefined

因此這裏的真正順序是

var log;
log(); //這時候只是聲明瞭log這個變量,並非函數,卻用函數的方法調用它,因此會報錯,說這不是一個函數。
log = function(mes){
    console.log(mes)
}

在function裏用var聲明變量

咱們雖然知道,var聲明的變量會提高,但並不知道會提高到哪一個程度。

在此以前來看一段代碼

var a = 4;

function foo(){
    var a = 5;
    console.log(a);

}
foo();

console.log(a)

答案是5,4,先輸出5,再輸出4。

var聲明的變量是有函數做用域的,因此foo裏的a和foo外面的a沒有任何關係,這種狀況正是我想要的。

再改下代碼

function foo(){
    a = 5
    console.log(a);
    var a;
}
foo();

console.log(a)

答案是5,a is not defined

第4行代碼輸出5,第9行報錯。

這種狀況就是變量提高只會提高到變量所在的 做用域的頂部,不會提高到父級做用域。

所以能夠得出一個結論:變量提高只會將變量提高到本身所在的做用域的頂部

函數優先

既然用varfunction的變量都有提高的功能,那若是同一個變量用這兩種都聲明會怎樣,好吧,看標題就知道了,函數優先。

具體看下代碼

foo();

var foo;

function foo(){
    console.log(1)
}

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

答案是1

這段代碼其實這樣子的

function foo(){
    console.log(1)
}

foo();// 1

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

仔細一看,var foo;沒了,沒錯,它被引擎忽略了,認爲重複聲明因此把它拋棄了。

好,既然var聲明的變量比不了函數聲明,那就用函數聲明,屢次聲明同個變量。

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

foo()

foo聲明瞭三次,調用了四次,每次調用的結果都是3,因此最後的函數聲明會覆蓋以前的函數聲明

可是var還想掙扎一下,以爲仍是有必要證實本身的存在感的。

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

foo()

仔細看,中間那部分代碼改了,依次輸出3,3,2,2

雖然var foo被忽略了,但下面的函數仍是有用的,這段代碼能夠當作是這樣的

function foo(){
    console.log(3);
}

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

在普通塊內部聲明函數

以前是在做用域聲明函數,如今來塊裏面聲明函數

function foo(){

    console.log(b); // undefined
    b(); //TypeError: b is not a function

    var a = true;

    if(a){
        function b(){
            console.log(2)
        }
        //下面這段代碼和上面的結果同樣
        // var b = function(){
  //           console.log(2)
  //       }
    }
    //b() --> 這裏會被執行

}

foo()

從上面看上去,b是undefined,證實這個變量仍是有的,只不過它並非一個函數,這狀況和用函數表達式差很少。

總結

  • 提高分爲函數聲明提高和變量聲明提高
  • 聲明變量用var,聲明函數用function
  • 變量提高會將變量提高到本身所在做用域的頂部
  • 函數表達式不存在提高的機制。
  • 函數聲明和變量聲明同時聲明同一個標識符時,函數聲明優先
  • 多個函數聲明同一個標識符時,最後一個聲明覆蓋先前的聲明
相關文章
相關標籤/搜索