這系列筆記是為了要回答 week16 作業而生的,因為要讓回答作業更完整,我決定把一些基礎知識以及還有在課程上面學到的觀念先整理起來,方便我回答作業,更方便我日後複習更有系統。
JavaScript 程式執行過程
在過去學 JavaScript (以下簡稱 JS) 基礎語法的時候,可能首要條件是讓程式結果如預期中呈現,重點在練習語法,而先不在意這些看似不重要的基礎。
但也是時候該來了解,整個 JS 執行的過程是如何,或許之後遇到的 bug,我們可以更快更有效率的 debug。
首先我們來看 MDN 對 JavaScript 的介紹 :
JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions. While it is most well-known as the scripting language for Web pages, many non-browser environments also use it, such as Node.js, Apache CouchDB and Adobe Acrobat. JavaScript is a prototype-based, multi-paradigm, single-threaded, dynamic language, supporting object-oriented, imperative, and declarative (e.g. functional programming) styles. Read more about JavaScript.
中文
JavaScript (簡稱 JS) 是具有一級函數 (First-class functions) 的輕量級、直譯式或即時編譯(JIT-compiled)的程式語言。它因為用作網頁的腳本語言而大為知名,但也用於許多非瀏覽器的環境,像是 node.js、Apache CouchDB。JavaScript 是一個基於原型的 (Prototype-based (en-US))、多範型的、動態語言,支援物件導向、指令式以及宣告式 (如函數式程式設計) 風格。 閱讀關於 JavaScript 以取得更多資訊。
我們可以從這段介紹以及我自己爬文搜尋到的資訊,整理出 JavaScript 幾個重要的特性。
High-Level:JS 為 High-level 語言,low-level 的語言代表是 C, 低階語言與電腦溝通比較容易,但與人溝通 (學習) 比較不容易,而在高階語言中像是 JavaScript 或 Python 比起低階語言更直覺,使我們人類比較容易學習, 但是犧牲的是效率沒有低階語言那麼。
感謝 助教WooooHuan,對於執行效率的補充:
關於這段高低階語言的討論,想進行一些補充,
這裡指的高低階語言,通常是指誰更靠近底層 (硬體)
所以語言的高低階並非絕對,而是一種相對概念,
比的往往是哪個語言與底層之間,有著更多層的抽象。而執行效率與語言屬於哪個階層,可能沒有太多直接的關連性,
很多靜態語言經過編譯後,其實都相當接近底層,執行起來效率並不會差太多,
如果要討論語言的執行效率,或許可以從語言隸屬於動態或靜態這點切入會更好!回收機制:在 JS 中,有個機制是可以自動刪除沒有在使用的 object,刪除後會釋放記憶體。
- Just-In-Time (interpreted):我們都知道和電腦溝通,我們只能用 0 和 1,所以我們在寫 JS 與電腦溝通的時候,是需要經過『翻譯』後,才能讓電腦懂我們在說什麼,總之不管什麼程式語言最後都是需要經過編譯,才能讓電腦動起來。
- multi-paradigm :不同於其他程式語言,JS 可以實現 Procedural programming, Object-oriented programming, Functional programming。(這部分我自己還不是搞得很清楚,先簡單帶過。)
- prototype-based:JS 中除了 primitive value 之外,都是 Object。而當我們使用 array 時,我們可以使用 push 去新增元素或者使用其他不同 array 的 method, 這就是利用 prototype 特性去使用 method。
- first-class functions:因為這個特性,所以我們可以把 function 當成像變數一樣傳過來傳過去的。
dynamic:當我們在宣告一個變數的時候,我們並沒有去指定此變數的類型( number, string,...),就算一開始將一個 number 賦值給變數,之後又可以改成放字串,例如:
var a = 666 a = "OMG"
- single-threaded : 簡單的說就是 JS 在同一時間只能做一件事情,但是經過 event loop,我們可以使得延時的程序(例如:非同步的程序 setTimeout ... 。) 先擱著,等它完成後再來處理。
這邊就是幾個 JS 主要特性,或許列得不夠完整也講得不夠深入,但應該足夠等等來回答作業使用。
學習到這邊,我們都知道 JS 不只是可以在瀏覽器環境下執行,非瀏覽器下環境也是可以讓 JS 跑起來的( node.js,...)。
(以下假設是在瀏覽器環境下執行 JavaScript 來做解說,但其實概念大同小異!)
JavaScript Engine
JS Engine 的存在是讓我們可以把執行 JavaScript 程式碼,每個瀏覽器都有自己執行 JS 的 JS Engine。
JS Engine,做的事情很簡單,就是幫我們把程式碼『翻譯』給電腦就是了,在翻譯的過程中會去做幾件事,首先會先解析我們的程式碼,在這階段換先檢查我們的語法是否有錯誤,以及是否有宣告變數(宣告而已還沒執行) => 再來才是真正的編譯成電腦會懂的語言(0,1),然後最後就是逐步執行我們寫的程式。(其實應該不是那麼簡單而已,但再探究下去就超出我能力範圍了,以後有空再來探討)
我們最常使用的 Chrome 瀏覽器就是使用 V8 Engine 來執行 JavaScript 的。
JS Engine 包含了很重要的東西 Call Stack
和 HEAP
。
Call Stack 是放著 Execution Context 的地方,也是執行程式的地方 。
Heap 就是內存記憶體,裡面就是放著程式需要的 Object, 變數 ... 等。
但是單有 JS Engine 並不足夠,這就像你現在只是擁有了火跟鍋子就說你擁有了廚房一樣,我們勢必需要流理台,烤箱,刀子...等等一堆工具來強大我們的廚房。所以在瀏覽器下執行 JS ,我們就需要 Web API,Web API 提供了許多功能讓我們可以在使用,像是 DOM, setTimeout, fetch API, 甚至是我們已經用到快爛掉的 console ... 等,Web API 提供的這些功能並不是 JavaScript 這語言本身的一部份,是 JavaScript 透過 window 這個 Object 來使用 Web API 提供的功能。
簡單的說,在瀏覽器環境 (runtime) 下執行 JavaScript 的話,這個環境中就會包含了幾個主要的東西, JS Engine, Web API, 還有 Callback Queue, 裡面是放 callback function,例如像是監聽 click 事件的 handler function,或是 setTimeout 的 callback funtion,...等。
Event Loop
現在我們有了幾個關鍵的名詞介紹,Call Stack, Web API, Callback Queue 了,這樣我們就可以來認識建構在這幾個名詞上的 Event Loop。
剛剛提到的 Event Loop ,其實就是圍繞著 Call Stack, Web API, Callback Queue ,這幾個名詞上面,可以想像成瀏覽器是一個廚房,在廚房裡面煮菜我們會有幾個流程,Call Stack 裡面會放著完成一道菜的步驟, Web API 是其中某個步驟裡面的一項工作,就像是幫我們備料一樣,而Callback Queue 就像是 Web API 幫我們備完料的食材,準備放入 Call Stack 裡完成,最後 heap 不是不重要,而是他就只是個內存記憶體,就像是廚房裡那個放著一堆醬料的儲藏架或者只是個冰箱而已。
剛剛提到的 JS Engine 的編譯過程,還有一部份沒有提到,就是在正式執行之前,JS 會產生出 Execution Context (簡稱 EC),第一個產生的為 Global Execution Context
,global EC 裡面會放著我們需要的東西,例如變數的宣告,還有哪些是 function 的宣告,但要注意並不包含 function 的 body,也不包括賦值,可以把 EC 當成一個 object 來看的話就像是這樣:
var a = 666
function print() {
console.log(a)
}
print()
global EC {
a : undefined,
print: function
}
可以把 EC 想像成一個 JavaScript 完整程式碼中一小片段,裡面放著我們需要的資訊,或是模擬成要完成一道菜的食譜中的某個步驟,而 global EC 就是主廚,global EC 只會有一個,其他的 function EC 就像是二廚,廚助...。
global EC 為程式執行前第一個會生成的,再來遇到有 function 呼叫時才會再生成一個 EC,如果這個 EC 執行完畢就會消失,一直到連 global EC 都消失為止才是完整的結束。
Event Loop 的細節會在做 hw1 及 hw2 中再做更深入的解釋。