前端進階系列(七):什麼是執行上下文?什麼是調用棧?

原文做者:Valentino
原文連接:https://www.valentinog.com/blog/js-execution-context-call-stack

什麼是Javascript中的執行上下文?

我打賭你不知道答案。javascript

編程語言中最基礎的組成部分是什麼?java

變量和函數對嗎?每一個人均可以學習這些板塊。編程

但除了基礎知識以外還有什麼?瀏覽器

在稱本身爲中級(甚至是高級)Javascript開發人員以前,你應該掌握的Javascript的核心是什麼?數據結構

有不少:Scope(做用域)Closure(閉包)Callbacks(回調)Prototype(原型)等等。閉包

但在深刻研究這些概念以前,您至少應該瞭解Javascript引擎的工做原理。異步

在這篇文章中,咱們將介紹每一個Javascript引擎的兩個基本部分:執行上下文和調用堆棧。編程語言

(不要懼怕。它比你想象的容易)。ide

準備好了嗎?函數

目錄

  1. 你會學到什麼?
  2. Javascript如何執行您的代碼?
  3. Javascript引擎
  4. 它是如何工做的?
  5. 全局存儲器?
  6. 什麼是調用棧?
  7. 什麼是局部執行上下文?
  8. 總結

你會學到什麼?

在這篇文章中你將學到:

  • Javascript引擎是如何工做的?
  • Javascript中執行上下文
  • 什麼是調用棧
  • 全局執行上下文和局部執行上下文之間的區別

Javascript如何執行您的代碼?

經過查看Javascript內部功能,您將成爲更好的Javascript開發人員,即便您沒法掌握每個細節。

如今,看看下面的代碼:

var num = 2;

function pow(num) {
    return num * num;
}

如今告訴我:你認爲在瀏覽器裏以何種順序執行這段代碼?

換句話說,若是您是瀏覽器,您將如何閱讀該代碼?

這聽起來很簡單。

大多數人認爲「是的,瀏覽器執行功能pow並返回結果,而後將2分配給num。」

在接下來的部分中,您將發現那些看似簡單的代碼行背後的機制。

Javascript引擎

要了解Javascript如何運行您的代碼,咱們應該遇到第一件可怕的事情:

執行上下文

在Javascript中什麼是執行上下文?

每次在瀏覽器(或Node)中運行Javascript時,引擎都會執行一系列步驟。

其中一個步驟涉及建立全局執行上下文。

什麼是引擎?

也就是說,Javascript引擎是運行Javascript代碼的「引擎」。

現在有兩個突出的Javascript引擎:Google V8和SpiderMonkey。

V8是Google開源的Javascript引擎,在Google Chrome和Nodejs中使用。

SpiderMonkey是Mozilla的JavaScript引擎,用於Firefox。

到目前爲止,咱們有Javascript引擎和執行上下文。

如今是時候瞭解它們如何協同工做了。

它是如何工做的?

每次運行一些Javascript代碼是,引擎都會創造一個全局執行上下文。

執行上下文是一個比喻的詞,用於描述運行Javascript代碼的環境。

我以爲你很難想象出這些抽象的東西。

如今將全局執行上下文視爲一個框:

讓咱們再看看咱們的代碼:

var num = 2;

function pow(num) {
    return num * num;
}

引擎如何讀取該代碼?

這是一個簡化版本:

引擎:第一行,它是變量!讓咱們將它存儲在全局存儲器中。

引擎:第三行,我看到了一個函數聲明。讓咱們也把它存儲在全局存儲器中。

引擎:看起來我已經完成了。

若是我再次問你:瀏覽器如何「看到」如下代碼,你會怎麼說?

是的,它有點自上而下......

正如你所看到的那樣,引擎沒有運行功能pow!

這是一個函數聲明,而不是函數調用。

上面的代碼將轉換爲存儲在全局存儲器中的一些值:函數聲明和變量。

全局存儲器?

我已經對執行上下文感到困惑,如今還要問我什麼是全局存儲器?

接下來讓咱們看看什麼是全局存儲器

全局存儲器

Javascript引擎也有一個全局存儲器。

全局內存包含全局變量和函數聲明供之後使用。

若是您閱讀Kyle Simpson的「做用域和閉包」,您可能會發現全局存儲器與全局做用域的概念重疊。

實際上它們是一回事。

這是些很可貴概念。

但你如今不該該擔憂。

我但願你能理解咱們難題的兩個重要部分。

當Javascript引擎運行您的代碼時,它會建立:

  • 全局執行上下文
  • 全局存儲器(也稱爲全局做用域或全局變量環境)

一切都清楚了嗎?

若是我在這一點上,我會:

  • 寫下一些Javascript代碼
  • 當你是引擎時,一步一步地解析代碼
  • 在執行期間建立全局執行上下文和全局存儲器的圖形表示

您能夠在紙上或使用原型製做工具編寫練習。

對於個人小例子,圖片看起來以下:

在下一節中,咱們將看另外一個可怕的事情:調用棧。

什麼是調用棧?

您是否清楚地瞭解了執行上下文,全局存儲器和Javascript引擎如何組合在一塊兒?

若是沒有,花時間查看上一節。

咱們將在咱們的難題中介紹另外一篇文章:調用棧。

讓咱們首先回顧一下Javascript引擎運行代碼時會發生什麼。 它建立:

  • 全局執行上下文
  • 全局存儲器

除了咱們的例子,沒有更多的事情發生:

var num = 2;

function pow(num) {
    return num * num;
}

代碼是純粹的值分配。

讓咱們更進一步。

若是我調用該函數會發生什麼?

var num = 2;

function pow(num) {
    return num * num;
}

var res = pow(num)

有趣的問題。

在Javascript中調用函數的行爲使引擎尋求幫助。

這個幫助來自Javascript引擎的朋友:調用棧。

它聽起來可能並不明顯,但Javascript引擎須要跟蹤發生的狀況。

它依賴於調用棧。

什麼是Javascript中的調用棧?

調用棧就像是程序當前執行的日誌。

實際上它是一個數據結構:堆棧。

調用棧的工做原理是什麼?

不出所料,它有兩種方法:push和pop。

push是將某些東西放入堆棧的行爲。

也就是說,當您在Javascript中運行函數時,引擎會將該函數push到調用堆棧中。

每一個函數調用都被push到調用棧中。

push的第一件事是main()(或global()),它是Javascript程序執行的主要線程。

如今,上一張圖片看起來像這樣:

pop另外一端是從堆棧中刪除某些東西的行爲。

當函數執行結束時,將從調用棧中pop出去。

咱們的調用棧將以下所示:

如今?您已準備好從那裏掌握每一個Javascript概念。

請看下一部分。

局部執行上下文

到目前爲止,一切彷佛都很清楚。

咱們知道Javascript引擎建立了一個全局執行上下文和一個全局存儲器。

而後,當您在代碼中調用函數時:

  • Javascript引擎請求幫助
  • 這個幫助來自Javascript引擎的朋友:調用棧
  • 調用棧會跟蹤代碼中調用的函數

當你在Javascript中運行一個函數時,還有另外一件事情發生。

首先,該功能出如今全局執行上下文中。

而後,另外一個迷你上下文出如今函數旁邊:

那個迷你上下文叫作局部執行上下文。

若是您注意到,在上一張圖片中,全局存儲器中會出現一個新變量:var res。

變量res的值首先是undefined。

而後,只要pow出如今全局執行上下文中,該函數就會執行而且res將獲取其返回值。

在執行階段,建立局部執行上下文以保存局部變量。

記住這一點。

瞭解全局和局部執行上下文是掌握做用域和閉包的關鍵。

總結

Javascript引擎建立執行上下文,全局存儲器和調用棧。可是一旦你調用一個函數,引擎就會建立一個局部執行上下文。

常常被忽視的是,新的開發人員老是將Javascript內部視爲神祕的東西。

然而,它們是掌握高級Javascript概念的關鍵。

若是你學習執行上下文,全局存儲器和調用棧,那麼Scope,Closures,Callbacks和其餘東西將變得垂手可得。

特別是,理解調用堆棧是相當重要的。

一旦你想象它,全部的Javascript將開始有意義:你將最終理解爲何Javascript是異步的以及咱們爲何須要回調。

相關文章
相關標籤/搜索