(注1:如果有問題歡迎留言探討,一起學(xué)習(xí)!轉(zhuǎn)載請注明出處,喜歡可以點個贊哦P偷省)
(注2:更多內(nèi)容請查看我的目錄。)
1. 簡介
在本系列的第二篇文章JS入門難點解析2-JS的變量提升和函數(shù)提升中嘱函,我們已經(jīng)討論過甘畅。之所以不說JS需要編譯,只是它不像其他編譯語言一樣需要翻譯成等價的另一種語言。但是仍然需要進行語法分析和代碼生成疏唾,并且通常是立即執(zhí)行蓄氧。而且,JS的變量提升和函數(shù)提升就發(fā)生在編譯階段槐脏。
回顧一下:
var foo = function () {
console.log('foo1');
}
foo(); // foo1
var foo = function () {
console.log('foo2');
}
foo(); // foo2
以及
function foo() {
console.log('foo1');
}
foo(); // foo2
function foo() {
console.log('foo2');
}
foo(); // foo2
這兩段代碼喉童,前一段進行了變量聲明提升,后一段進行了函數(shù)聲明提升顿天。我們講到過堂氯,這是因為 JavaScript 編譯器和引擎并非一行一行地分析和執(zhí)行程序,而是一段一段地分析執(zhí)行牌废。當分析執(zhí)行一段代碼的時候蜜托,會進行一個“準備工作”效览,包括變量聲明提升和函數(shù)聲明提升等。那么這里所謂的一段指的是什么呢获印?到底JavaScript編譯器和引擎遇到一段怎樣的代碼時才會做“準備工作”呢谬哀?這就需要了解什么是可執(zhí)行代碼了铜异。
2. 可執(zhí)行代碼
JavaScript 的可執(zhí)行代碼(executable code)有以下三類:全局代碼败京、函數(shù)代碼陪腌、eval代碼。
當JS引擎遇到這三類代碼時莫绣,會開始做準備工作,創(chuàng)建一個“執(zhí)行上下文(execution context)"悠鞍。
舉例說明对室,當JS執(zhí)行到一個函數(shù)的時候,就會創(chuàng)建該函數(shù)的“執(zhí)行上下文(execution context)"咖祭。那么問題來了掩宜,JS代碼中可能出現(xiàn)為數(shù)眾多的函數(shù),如何管理創(chuàng)建的那么多執(zhí)行上下文呢么翰?
3. 執(zhí)行上下文棧
JavaScript 引擎創(chuàng)建了執(zhí)行上下文棧(Execution context stack牺汤,ECS)來管理執(zhí)行上下文。
為了模擬執(zhí)行上下文棧的行為浩嫌,讓我們定義執(zhí)行上下文棧是一個數(shù)組:
ECStack = [];
試想當 JavaScript 開始要解釋執(zhí)行代碼的時候檐迟,最先遇到的就是全局代碼,所以初始化的時候首先就會向執(zhí)行上下文棧壓入一個全局執(zhí)行上下文码耐,我們用 globalContext 表示它追迟,并且只有當整個應(yīng)用程序結(jié)束的時候,ECStack 才會被清空骚腥,所以 ECStack 最底部永遠有個 globalContext敦间。
ECStack = [
globalContext
];
現(xiàn)在 JavaScript 遇到下面的這段代碼了:
function fun3() {
console.log('fun3')
}
function fun2() {
fun3();
}
function fun1() {
fun2();
}
fun1();
執(zhí)行一個函數(shù)的時候,就會創(chuàng)建一個執(zhí)行上下文,并且壓入執(zhí)行上下文棧廓块,當函數(shù)執(zhí)行完畢的時候厢绝,就會將函數(shù)的執(zhí)行上下文從棧中彈出。知道了這樣的工作原理带猴,讓我們來看看如何處理上面這段代碼:
// 偽代碼
// fun1()
ECStack.push(<fun1> functionContext);
// fun1中調(diào)用了fun2昔汉,還要創(chuàng)建fun2的執(zhí)行上下文
ECStack.push(<fun2> functionContext);
// fun2還調(diào)用了fun3
ECStack.push(<fun3> functionContext);
// fun3執(zhí)行完畢
ECStack.pop();
// fun2執(zhí)行完畢
ECStack.pop();
// fun1執(zhí)行完畢
ECStack.pop();
// javascript接著執(zhí)行下面的代碼,但是ECStack底層永遠有個globalContext