深刻解析變量聲明提高和函數聲明提高

JS運行階段

JavaScript的運行階段分爲預編譯階段執行階段,今天要討論的變量聲明提高和函數聲明提高,就是在這個階段完成的。函數

在預編譯階段,JS引擎會作一件事情,那就是讀取變量的定義肯定其做用域即生效範圍。code

  • 變量定義ip

    • 使用var關鍵字定義的變量,並未賦值的狀況下,該變量的值是undefined
  • 變量做用域作用域

    • 全局變量的做用域遍及全局
    • 局部變量的做用域僅在於函數內部
    • 函數內部的同名變量或參數其優先級高於全局同名變量

變量聲明提高

先看一個簡單示例:io

var name = 'ryan';

function say(){
  console.log(name); //輸出:undefined
  var name = 'zoe';
  console.log(name); //輸出:'zoe'
}

say();

解析:上述代碼從結果看,say函數執行第一次打印name時,並未打印全局的name('ryan'),而是打印局部的name(undefined),這是由於在預編譯階段,say函數內部進行了變量聲明提高,提高後的執行效果以下:console

var name = 'ryan';

function say(){
  var name; //變量name聲明提高至做用域頂部,但未賦值,故爲undefined
  console.log(name); //存在局部name,則無視全局name
  name = 'zoe'; //變量賦值保持原位
  console.log(name); //輸出:'zoe'
}

say();

函數聲明提高

函數的兩種建立方式:編譯

  • 函數聲明
  • 函數表達式

函數聲明:function

say(); //輸出:'saying'

function say(){
  console.log('saying');
}

函數表達式:變量

say(); //報錯:say is not a function

var say = function(){
  console.log('saying');
}

解析:一樣地先執行函數,後建立函數,結果倒是不同。緣由在於,經過函數聲明的方式,該函數聲明(包括定義)會被提高至做用域的頂部,而表達式的建立方式則只提高了變量say至做用域的頂部,此時的say其值爲undefined,調用say()天然報錯「say不是一個方法」。方法

再來看一個示例:

var say = function(){
  console.log('1');
};

function say(){
  console.log('2');
};

say(); //輸出:'1'

解析:預編譯階段進行變量聲明提高和函數聲明提高後,上述代碼執行效果等同於:

var say; //變量聲明提高

function say(){ //函數聲明提高
  console.log('2');
}

say = function(){ //變量賦值保持原位執行,say函數被覆蓋
  console.log('1');
};

say(); //輸出'1'

總結:函數聲明提高,會將函數的聲明和定義全都提高至做用域頂部。
變量聲明提高,只提高聲明部分(未賦值狀態),賦值部分保持原位置不動。

變量聲明和函數聲明提高的優先級

函數聲明提高的優先級要高於變量聲明提高。

先看一個簡單示例:

console.log(say); //輸出:[Function: say]

function say(){
  console.log('1');
};

var say = '2';

console.log(say); //輸出'2'

解析:本例中聲明的函數和變量同名都是say,且函數聲明在先,變量聲明在後,按理說第一次打印say值預期會是undefined,然而結果是[Function: say]。

預編譯階段進行變量聲明提高和函數聲明提高後,上述代碼執行效果等同於:

var say = function (){ //函數聲明(包括定義)提高
  console.log('1');
};

var say; //只是聲明,並不會覆蓋say的值

console.log(say); //故輸出:[Function: say]

say = '2'; //此時say會被覆蓋

console.log(say); //輸出'2'

總結:同名狀況下,函數聲明提高優先級要高於變量聲明提高,且提高後該函數聲明定義不會被提高後的同名變量聲明所覆蓋,可是會被後續順序執行的同名變量賦值所覆蓋。

相關文章
相關標籤/搜索