在咱們前端筆試的過程當中幾乎都會遇到函數聲明提高或者變量聲明提高的題目,是否是不少人知道這個知識點可是分析的時候仍是會出現不一樣程度的錯誤呢,接下來跟我一步步走下去,讓你們之後不再用擔憂這類題目帶來的困擾。javascript
咱們必需要了解javascript有2個特色,單線程和解釋性語言,能夠理解解釋性語言是翻譯一句執行一句,那麼在js運行時是通過如下三步:前端
語法分析java
預編譯web
解釋執行函數
說明:語法分析就是通篇掃描一遍是否有語法錯誤,沒有錯誤進行預編譯階段,而後進行解釋執行。
複製代碼
eg: 這裏只是簡要說明下以上亮點。
console.log(window.a)
console.log(window.b)
function testGlobal(){
var a = b = 123;
}
testGlobal()
console.log(b)
console.log(a)
eg: 按照咱們思惟解釋一行編譯一行,沒有任何問題。
function test(){
console.log("test")
}
test() // test
eg: 按照咱們思惟解釋一行編譯一行,應該報錯呀,爲何能正常打印出結果呢。
test1() // test1
function test1(){
console.log("test1")
}
eg:
var test3 = 5
console.log(test3) // 5
eg: 爲何能輸出undefined
console.log(test4) //undefined
var test4 = 6
eg: 爲啥這樣就報錯。
console.log(test5) // error
複製代碼
通過以上的例子,不少人會快速的總結兩句話:ui
這2句正確可是不能解決咱們全部問題,很少說,用例子說話:spa
eg: 這裏我就不寫答案了,本身能夠試一試是否掌握。
console.log(a);
function a(a){
console.log(a);
var a = 123;
var a = function(){};
console.log(a);
}
a(123);
var a = 123;
eg: 這裏我就不寫答案了,本身能夠試一試是否掌握。
console.log(a);
function a(a){
console.log(a);
a = 123;
function a(){};
console.log(a);
}
a(123);
var a = 123;
複製代碼
若是感受有點暈乎,那麼咱們瞭解預編譯則能夠輕鬆解答問題。線程
記住預編譯發生在函數執行的前一刻,咱們記住如下四部曲便可:翻譯
- 建立AO對象;
- 找形參和變量聲明,將變量和形參做爲AO對象的屬性名,值爲undefined;
- 將實參值和形參統一;
- 在函數體裏面找到函數聲明,值賦予函數體。
那麼有人會問了,函數執行是這樣,那麼全局定義的變量聲明和函數聲明會是怎樣呢,其實同樣,不一樣的就是第一步,全局建立GO對象,GO也就是等於window。code
eg: 咱們先看函數執行
function fn(a){
console.log(a);
console.log(b);
console.log(c);
var a = 123;
function a(){};
console.log(a);
var b = function(){};
console.log(b);
function c(){};
}
fn(111);
求解:對應以上步驟來
第1步: 建立AO對象
AO{
}
第2步: 找形參和變量聲明,將變量和形參做爲AO對象的屬性名,值爲undefined,若是形參和變量聲明或者函數聲明有同名的則寫一個就好。
AO{
a: undefined,
b: undefined,
c: undefined
}
第3步: 將實參值和形參統一;
AO{
a: 111,
b: undefined,
c: undefined
}
第4步: 在函數體裏面找到函數聲明,值賦予函數體。
AO{
a: function a(){},
b: undefined,
c: function c(){}
}
第5步: 在按照順序執行,提高的和賦值過的咱們就不用看了。
fn(111);
console.log(a) // function a(){} a ==> AO.a
console.log(b) // undefined
console.log(c) // function c(){}
a =123 // 這裏只剩下賦值 A0.a = 123
console.log(a) // 123
b = function(){}; // AO.b = function(){}
console.log(b) // function(){}
複製代碼
總結說下第五步,這就是剩下執行的語句了,爲啥變量聲明和函數聲明都沒有了呢,就是前面4步已經執行了,這下是否是有點恍然大悟,原來如此簡單,咱們繼續多來幾個例子來講明,也便於增長記憶。
eg:
function fn1(a,b){
console.log(a);
console.log(b);
var b =234;
a = 123;
console.log(a);
function a(){};
var a;
b = 456;
var b = function(){}
console.log(a);
console.log(b);
}
fn1(222);
按照預編譯步驟來解:
第1步:
AO{}
第2步:
AO{
a: undefined,
b: undefined,
}
第3步:
AO{
a: 222,
b: undefined,
}
第4步:
AO{
a: function a(){},
b: undefined,
}
第5步: 解釋執行
fn1(222);
console.log(a) // function a(){}
cosnole.log(b) // undefined
b = 234; // AO.b = 234
a = 123 // AO.a = 123
console.log(a) // 123
b = 456; // AO.b = 456
b = function(){} // Ao.b = function(){}
console.log(a); // 123
console.log(b); // function(){}
複製代碼
如今通過以上2個案例是否是基本瞭解預編譯,是否是就不怕函數聲明提高和變量聲明提高呢,有人說了,要是全局變量聲明和有全局函數聲明又有函數體內函數聲明順序又是怎麼樣的呢?
記住全局生成GO對象,函數執行體生成AO對象。其餘規則同樣,繼續來例子說明:
eg:
console.log(global)
function global(){
console.log(global)
global = 200
console.log(global)
var global = 300
}
global()
console.log(global);
global = 100;
var global;
console.log(global);
第1步:
GO{}
第2步:
GO{
global: undefined
}
第3步:, 沒有
第4步:
GO{
global: global(){ ...}
}
第5步:解釋執行
console.log(global) // global(){ ...}
接下來要執行函數,那麼執行函數以前要走4步
第1步:
AO{}
第2步:
AO{
global: undefined
}
第3步: 沒有
第4步: 沒有
第5步:解釋執行
global()
console.log(global) // undefined 注意:AO對象找到則值爲AO的屬性值,沒有則去GO對象裏面去找
global = 200 // AO.global = 200
console.log(global) // 200
global = 300 // AO.global = 300
global = 100; // GO.global = 100
console.log(global) // 100 這裏是全局的global
eg: 這一題有了if,其實預編譯我只管找你聲明,其餘無論,那麼又和以上步驟相同,還有就是沒有聲明就賦值則掛在GO對象上
function test(){
console.log(a); // undefined AO對象沒有,則到GO對象中找
console.log(b); // undefined
if(a){
var b = 100; // 不執行b的賦值
}
console.log(b); // undefined
c = 234;
console.log(c) //234
}
var a ;
test();
a = 10;
console.log(c) //234
解答,這裏我就簡要的寫最終兩個對象的值:
GO{
a: 10
c: 234
}
AO{
b: undefined,
}
因此答案很明顯.
複製代碼
通過以上總結,那咱們是否是能夠找到規律: