原文: https://javascript.plainengli...
這篇部落格通過動畫的形式展現js的執行過程,對理解js是如何工作的很有幫助。本篇翻譯會對原文翻譯有所刪減,剔除一些廢話,直接翻譯核心的講解部分。另外水平有限,如果翻譯有不對的地方,歡迎指正!
執行上下文
Everythig in Javascript happens inside an Execution Context
Javascript中的一些都發生在執行上下文中。
我希望大家都能記住這句話,因為它非常重要!你可以假設執行上下文是一個大的容器,當瀏覽器想要執行js程式碼時會呼叫它。在這個容器中有兩個元件:
- 記憶體元件
- 程式碼元件
記憶體元件
記憶體元件也叫變數環境,在記憶體元件中,變數和函式儲存成 鍵值對 的形式
程式碼元件
在程式碼元件中,程式碼會一行一行的執行,程式碼元件還有一個別名叫做 執行執行緒
javascript是同步的單執行緒語言。因為它一次只能以特定的順序執行一個命令。
程式碼執行
var a = 2;
var b = 4;
var sum = a + b;
console.log(sum);
在這段程式碼中,初始化了兩個變數a,b,然後將a和b相加的值賦值給了變數sum,我們來看看這段程式碼是怎麼執行的。
瀏覽器建立了一個全域性執行上下文,全域性上下文中包含了剛剛上文說到的記憶體元件還有程式碼元件。瀏覽器會分兩個階段執行這段程式碼。
- 記憶體建立階段
- 程式碼執行階段
記憶體建立階段
在記憶體建立階段,js會掃描所有的程式碼,併為變數和函式分配記憶體。對於變數來說,在變數建立階段,它們會被儲存成undefined。至於函式,js會保留整個函式程式碼,在後面的例子中會講到。
程式碼執行階段
在程式碼執行階段,js會逐行遍歷所有的程式碼。在記憶體中,直到a被分配為2之前,a的值一直都為undefined。b也是同樣的。然後js會將a和b相加的值6,存入記憶體中。然後列印sum值,最後銷燬全域性執行上下文。
函式在執行上下文中是怎麼被呼叫的
js中的函式與其他程式語言的函式工作方式是不同的。舉個例子?
var n = 2;
function square(num) {
var ans = num * num;
return ans;
}
var square2 = square(n);
var square4 = square(4);
上面的函式square接收一個引數num並返回num的平方。
在記憶體建立階段,js建立了一個全域性執行上下文,並且給變數和函式分配記憶體。函式將會被整個儲存在記憶體中。
在程式碼執行階段,js分配2值給了變數a,然後遇到函式,此時函式已經分配了記憶體,會直接跳到第6行,當執行函式square時,js又會在全域性執行上下文下建立一個新的執行上下文。
在新的執行上下文中又會經歷上文提到的兩個階段。在新執行上下文的記憶體建立階段,給num,ans分配記憶體。
分配完成後,開始程式碼執行階段,將n此時的值2分配給num,然後計算平方後將值分配給ans,然後返回值,再分配給square2。函式完成返回後會立馬銷燬它的執行上下文。
然後同樣的方式處理square4。
所有的程式碼執行完畢後,銷燬全域性執行上下文。
呼叫棧(call stack)
在js中呼叫函式時,js會建立一個執行上下文,當函式存在巢狀的情況是,執行上下文將變得非常複雜。js會通過呼叫棧管理執行上下文的建立與刪除。
棧
棧是一個有序專案集合,只能在一端進行插入和刪除操作。類似於堆書。
function a() {
function insideA() {
return true;
}
insideA();
}
a();
上面程式碼中,建立了一個函式a,a函式裡面又呼叫了一個返回true的函式insadeA。下面來分析下這段程式碼的執行過程
- js建立一個全域性執行上下文,然後分兩階段,給函式a分配記憶體,然後執行程式碼呼叫函式a。
- 呼叫函式後,又建立一個新的執行上下文(second),這個新的執行上下文,放置在全域性執行上下文上面。
- 執行second執行上下文的程式碼,是呼叫函式insideA
- 然後建立新的執行上下文(third),放置在second上,然後分配記憶體,執行程式碼返回true;
- 開始依次銷燬third,second,全域性global執行上下文。
最後
最近準備每週翻譯一篇文章,文章已放入github,如果有願意加入翻譯的同學,可與我聯絡。