JavaScript的運行階段分爲預編譯階段
和執行階段
,今天要討論的變量聲明提高和函數聲明提高,就是在這個階段完成的。函數
在預編譯階段,JS引擎會作一件事情,那就是讀取變量的定義
並肯定其做用域
即生效範圍。code
變量定義ip
變量做用域作用域
先看一個簡單示例: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'
總結:同名狀況下,函數聲明提高優先級要高於變量聲明提高,且提高後該函數聲明定義不會被提高後的同名變量聲明所覆蓋,可是會被後續順序執行的同名變量賦值所覆蓋。