瞭解js的運行機制有助於咱們在平常的工做中,寫成高質量的代碼,減小bug的產生,節約維護成本。也有助於咱們經過造火箭的面試。 javascript
瞭解運行機制以前,咱們先來搞清楚幾個基本概念。css
渲染是根據描述或者定義構建一個數據模型,生成圖形的過程。渲染引擎將頁面資源(html、css、javaScript等)構建成可視化、可聽化的多媒體結果。也就是咱們看到的瀏覽器網頁呈現。 html
當咱們在運行一段代碼時,真正賦予這段代碼生命的就是JavaScript引擎。JavaScript引擎是一個專門處理JavaScript腳本的虛擬機,通常會附帶在網頁瀏覽器中。JavaScript引擎從頭至尾負責整個JavaScript程序的編譯和執行過程。java
最爲你們熟知的無疑是V8引擎,他用於Chrome瀏覽器和Node中。node
若是想讓一段JavaScript代碼真正的運氣起來,單單靠JavaScript引擎是不夠的,JavaScript Engine的工做是編譯並執行 JavaScript 代碼,完成內存分配、垃圾回收等,可是缺少與外部交互的能力。面試
好比單靠一個V8引擎是沒法進行ajax請求、設置定時器、響應事件等操做的,這就須要JavaScript運行時(JavaScript Runtime)的幫助,它爲 JavaScript 提供一些對象或機制,使它可以與外界交互。ajax
好比,雖然Chrome和node都是用了V8引擎,可是他們的運行時卻不一樣,好比process、fs瀏覽器都沒法提供。瀏覽器
一段javaScript代碼的運行咱們能夠分爲兩個階段。bash
接下里咱們主要說說,JavaScript的執行階段。數據結構
JavaScript既是編譯語言,又是解釋語言。JavaScript引擎實際上在執行代碼前僅幾微秒就編譯了代碼。
稱爲JIT(及時編譯)。它自己是一個很大的話題。可是如今,咱們能夠跳過編譯背後的理論,而只關注執行階段,這仍然頗有趣。
JavaScript引擎,編譯和解釋咱們的JavaScript代碼。JavaScript引擎其實也包含了不少較小的部分,這些較小的部分,分工合做來保證JavaScript的運行。
先看一段代碼
var num = 2;
function pow(num) {
return num * num;
}
複製代碼
看到這段代碼,你們思考一下會發生什麼。可能你們已經想到JavaScript引擎,在執行到第一行代碼時就馬上講引用放入全局內存(Global Memory)。全局內存是JavaScript引擎保存變量和什麼函數的地方。當引擎讀取以上代碼時,全局內存將填充兩個綁定:
上面的代碼不會執行,接下來咱們嘗試執行函數。var num = 2;
function pow(num) {
return num * num;
}
pow(num);
複製代碼
當咱們執行函數的時,JavaScript引擎會用到調用堆棧(Call Stack)。調用堆棧是一個堆棧類的數據結構,意味着它是先進後出的執行方式。若是是多個函數,將依次進棧,先進後出。
打開瀏覽器控制檯,而後查看「來源」標籤。您將看到一些框,其中一個更有趣的名稱是Call Stack。當代碼塊在執行時,JavaScript引擎會建立一個執行上下文,已做爲代碼運行的基礎運行環境。
在"4.2.1代碼塊",有三種代碼塊,分別對應三種執行上下文
基礎執行上下文,一個程序只有一個全局執行上下文,任何不在函數內部的代碼都在全局執行執行上下文。全局執行上下文只要作兩件事情:
若是咱們的函數有一些嵌套變量或一個或多個內部函數怎麼辦?
var num = 2;
function pow(num) {
var a = 1,
b = 2,
c = 3;
function add(a, b, c) {
return a + b + c;
}
}
複製代碼
每當一個函數被調用時,都會爲該函數建立一個新的上下文。每一個函數都有它本身的執行上下文,不過是在函數被調用時建立的。函數上下文能夠有任意多個。每當一個新的執行上下文被建立。
執行在 eval 內部的代碼也會有它屬於本身的執行上下文,請不要、不要、不要輕易使用它。
執行上下文也分爲建立和執行階段。在建立階段就很是有意思了。
執行上下文的建立階段主要作了三件事:
在建立可執行上下文的時候,根據代碼的執行條件,來判斷分別進行默認綁定、隱式綁定、顯示綁定等。
this綁定也是有優先級的,優先級規則以下:
詞法環境是JavaScript引擎內部用來跟蹤標識符和特定變量之間的映射關係。詞法環境是Js做用域的實現機制。若是以前瞭解過做用域概念的話,和詞法環境是相似的(ES6以後做用域概念變爲詞法環境概念)。
做用域就是一個獨立的地盤,讓變量不會外泄、暴露出去。也就是說做用域最大的用處就是隔離變量,不一樣做用域下同名變量不會有衝突。 ES6 以前 JavaScript 沒有塊級做用域,只有全局做用域和函數做用域。ES6的到來,爲咱們提供了‘塊級做用域’,可經過新增命令let和const來體現。
詞法環境中含有外部詞法環境的引用,咱們能夠經過這個引用獲取外部詞法環境的變量、聲明等,這些引用串聯起來一直指向全局的詞法環境,所以造成了做用域鏈。
詞法環境中含有外部詞法環境的引用,咱們能夠經過這個引用獲取外部詞法環境的變量、聲明等,所以造成了閉包。
查看大量資料都沒有詳細的記錄變量環境。
ES5標準文檔中規定,執行環境包括:詞法環境、變量環境、this綁定。其中執行環境的詞法環境和變量環境組件始終爲詞法環境對象。當建立一個執行環境時,其詞法環境組件和變量環境組件最初是同一個值。在該執行環境相關聯的代碼的執行過程當中,變量環境組件永遠不變,而詞法環境組件有可能改變。
變量環境的不變和詞法環境的可能改變都是指引用的改變,規範12.10和12.14兩部分的內容提到了詞法環境在with以及catch語句塊中會改變。
JavaScript既是編譯語言,又是解釋語言。可是JavaScript本質上是一種解釋型語言,與編譯型語言不一樣的是它須要一邊執行一邊解析,而編譯型語言在執行時已經完成編譯,可直接執行,有更快的執行速度。
參考: