目錄
語法/詞法分析
預編譯
解釋執行
預編譯什么時候發生
js運行三步曲
預編譯前奏
預編譯步驟
鞏固基礎練習
語法/詞法分析
按語句塊的粒度解析成抽象語法樹 ,分析該js腳本代碼塊的語法是否正確,如果出現不正確,則向外拋出一個語法錯誤(SyntaxError),停止該js代碼塊的執行,然后繼續查找并加載下一個代碼塊;如果語法正確,則進入預編譯階段, 此時不涉及到運行時;
預編譯
預編譯分為全局預編譯和局部預編譯,全局預編譯發生在頁面加載完成時執行,而局部預編譯發生在函數執行的前一刻。 預編譯階段發生變量聲明和函數聲明,沒有初始化行為(賦值),匿名函數不參與預編譯 。只有在解釋執行階段才會進行變量初始化 。
解釋執行
js引擎解析代碼,解析一行執行一行 ;? 將實參帶入形參, 直接運行編譯好的代碼塊 ;
預編譯什么時候發生
預編譯分為全局預編譯和局部預編譯,全局預編譯發生在頁面加載完成時執行,而局部預編譯發生在函數執行的前一刻。
tip:預編譯階段發生變量聲明和函數聲明,沒有初始化行為(賦值),匿名函數不參與預編譯 。只有在解釋執行階段才會進行變量初始化 。
js運行三步曲
- 語法分析
- 預編譯
- 解釋執行
預編譯前奏
imply global暗示全局變量,任何變量,如果變量未經聲明就賦值,這些變量就為全局對象所有。一切聲明的全局變量和未經聲明的變量,全歸window所有。
例如:
var a = 123;
window.a = 123;
下面這個函數里面只有一個連等的操作,賦值操作都是自右向左的,而b是未經聲明的變量,所以它是歸window的,我們可以直接使用window.b去使用它。
function test(){// 這里的b是未經聲明的變量,所以是歸window所有的。var a = b = 110;
}
預編譯步驟
首先JavaScript的執行過程會先掃描一下整體語法語句,如果存在邏輯錯誤或者語法錯誤,那么直接報錯,程序停止執行,沒有錯誤的話,開始從上到下解釋一行執行一行。
局部預編譯的4個步驟:
- 創建AO對象(Activation Object)執行期上下文。
- 找形參和變量聲明,將變量和形參名作為AO屬性名,值為undefined
- 將實參值和形參統一。
- 在函數體里面找函數聲明,值賦予函數體。
全局預編譯的3個步驟:
- 創建GO對象(Global Object)全局對象。
- 找變量聲明,將變量名作為GO屬性名,值為undefined
- 查找函數聲明,作為GO屬性,值賦予函數體
由于全局中沒有參數的的概念,所以省去了實參形參相統一這一步。
tip:GO對象是全局預編譯,所以它優先于AO對象所創建和執行
鞏固基礎練習
關于AO對象的例子
// 函數
function fn(a){console.log(a);// 變量聲明+變量賦值(只提升變量聲明,不提升變量賦值)var a = 123;console.log(a);// 函數聲明function a(){};console.log(a);// 函數表達式var b = function(){};console.log(b);// 函數function d(){};
}
//調用函數
fn(1);
1.預編譯第1步:創建AO對象
AO{ }
2.預編譯第2步:找形參和變量聲明,將形參名和變量名作為AO對象的屬性名
AO{a : undefined,b : undefined
}
3.預編譯第3步:將實參值和形參統一
AO{a : 1,b : function(){...}
}
4.預編譯第4步:在函數體里面找函數聲明,值賦予函數體。
AO{a : function a(){...},b : undefined,d : function d(){...}
}
最后輸出結果:
// 函數
function fn(a){console.log(a); //根據AO對象中的數據第一個打印的是:fn()// 變量聲明+變量賦值(只提升變量聲明,不提升變量賦值)var a = 123; // 執行到這時,由于變量賦值是不提升的,所以函數被123覆蓋了console.log(a); // 123// 函數聲明function a(){}; // 這里被提升上去了,可以忽略console.log(a); // 123// 函數表達式var b = function(){};console.log(b); // 根據AO對象中的數據:fn()// 函數function d(){};
}
//調用函數
fn(1);
函數執行完畢,銷毀AO對象。
關于GO對象的例子
global = 100;
function test(){console.log(global); var global = 200;console.log(global);var global = 300;
}
test();
var global;
1.全局預編譯第1步:創建GO對象
GO{}
2.全局預編譯第2步:找變量聲明,將變量名作為GO屬性名,值為undefined
GO{global:undefined
}
3.全局預編譯第3步:查找函數聲明,作為GO屬性,值賦予函數體
GO{
global:undefined
}
4.局部預編譯第1步:創建AO對象
AO{}
5.局部預編譯第2步:找形參和變量聲明,將形參名和變量名作為AO對象的屬性名
AO{global: undefined
}
6.局部預編譯第3步:將實參值和形參統一(此函數沒有形參)
AO{global: undefined
}
7.局部預編譯第4步:在函數體里面找函數聲明,值賦予函數體。(此函數內沒有函數聲明)
AO{global: undefined
}
最的結果:
global = 100;
function test(){console.log(global); // 根據AO對象中的數據:undefinedvar global = 200; // 執行到這時,200覆蓋了undefinedconsole.log(global); // 200var global = 300;
}
test();
var global;
tip:關于GO對象和AO對象,它們倆是一個種鏈式關系,就拿上面的這個例子來說吧,如果在函數體的內部沒有定義global變量,這也意味著AO對象中將有這個global這個屬性。那如果沒有會怎么辦?它會去GO對象中尋找,說白了也就是一種就近原則。
?