iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 2
5
Modern Web

JavaScript基本功修煉系列 第 2

JavaScript基本功修練:Day2 - 瀏覽器與JavaScript引擎

  • 分享至 

  • xImage
  •  

這篇會簡述JavaScript的用途、瀏覽器渲染和JavaScript引擎的知識。

簡述JavaScript用途

在瀏覽器上使用JavaScript能夠為網頁加入互動元素,與使用者進行互動。例如以下的一些例子:

  • 操控及更新HTML和CSS
  • 按使用者的行為(例如是點擊按鈕、滑鼠滑動等行為)作出反應
  • 處理使用者輸入的資料,例如進行計算、寄存、進行驗證等等
  • 傳送要求給伺服器,獲取資料以及更新網頁內容
  • 控制cookies

網頁前端開發的基本(HTML、CSS、JavaScript):

  • HTML:標記或定義在網頁裏每一部分的內容。例如告訴瀏覽器哪個部分是段落文字、哪部分是圖片等等
  • CSS:設定樣式。例如設定文字大小、背景圖片、按鈕顏色
  • JavaScript:按使用者行動而進行互動。例如當按鈕被按下時會有視窗彈出

換言之,JavaScript就是能夠把靜態網頁動起來。

雖然JavaScript初期是用在網頁互動上,但它現時不只局限在瀏覽器,也能應用在伺服端(例如node.js)、手機app開發、遊戲開發、互動藝術等等。

JavaScript的限制

為了保障使用者安全及隱私,在客戶端瀏覽器的JavaScript沒法做以下的事:

  • 讀取或修改存放在使用者電腦的檔案,以及執行或複製在使用者的電腦裏的程式。例外是JavaScript能讀取在該網頁的cookies。
    (只有在使用者同意的情況下才可以處理使用者的文件,例如使用者把他的文件拖放到網頁進行上傳的動作)
  • 不能操控其他域名不同的網頁,也不能提取它的資料。即使使用者的瀏覽器同時開啟多個網頁,它們之間都不能干涉對方,提取對方在伺服器的資料。
  • 某些與使用者的互動,例如電腦鏡頭、麥克風等,需要事先得到使用者的允許。

DOM

當載入一個網頁時,瀏覽器會產生一個DOM去作解析,像樹狀圖一樣依序引伸去到不同的節點,並渲染到網頁上。在JavaScript的課題裏,我們經常都會操控某個DOM元素,例如經常會用document.getElementById()document.querySelector()去抓取某個DOM元素來用。詳細關於DOM的講解可看看這篇鐵人篇文章


截圖自W3schools

渲染次序

瀏覽器從上到下的次序來執行程式碼。通常我們都會把JavaScript檔案放在</body>前面,當瀏覽器跑完前面的HTML程式碼,才去跑JavaScript的檔案:

<body>
    <!-- HTML程式碼 -->
    <script src="all.js"></script>
</body>

如果我們在JavaScript檔案後面放一些HTML程式碼,瀏覽器會先跑完JavaScript檔案,才會再跑那些HTML程式碼,例如:

<body>
    <h1>標題一</h1>
    <script src="all.js"></script>
    <h2>跑完all.js才渲染我吧</h2>
</body>

all.js檔案:

alert('先執行我吧')

這個例子會先渲染h1,再彈跳出alert「先執行我吧」的視窗,最後才渲染h2

JavaScript如何在瀏覽器運作?

在瀏覽器上執行JavaScript時,需要透過瀏覽器本身附帶的JavaScript的引擎去進行解讀。例如Chrome和Opera用V8引擎,IE/Edge用Chakra引擎。


圖片來源: wikipedia

JavaScript普遍被介定為直譯型語言,而非編譯型,當我們寫完一段JavaScript後,並不會馬上進行編譯,而是把我們寫和看得懂的JavaScript(高階語言),一行一行地被轉成機器看得懂的語言(低階語言),並且執行。

  • 編譯型(Compiled language):
    程式碼在被執行前,整個程式碼會先被編譯器轉成機器語言,再拿去執行。若程式碼有錯,在預先編譯的過程中會報錯,而程式不會被執行。例子:Java,C++。
  • 直譯型(Interpreted language):
    程式碼一行一行被執行,並一行一行轉成機器語言,整個過程是一邊執行一邊進行編譯。若程式碼有錯,程式碼仍然會被執行,執行後才會回報錯誤。例子:Python,PHP。

在編譯上,JavaScript省去了先編譯後執行的動作,但在顯示速度上,當處理大量JS程式碼時,直譯型會比編譯型慢。

回到重點,到底JavaScript被引擎解譯的過程是怎樣?

JavaScript引擎運作流程:

  1. 語法分析器(Parser)
  2. 抽象語法樹(AST)
  3. 直譯器(Interpreter)
  4. 性能分析(Profiler)+ 優化編譯器(Compiler)

1. 語法分析器(Parser)

JavaScript的程式碼會被轉為一堆UTF-16字符編碼,再把它解析成一個個字詞,分辨它們之間的關係。例如分辨哪個部分是關鍵字(keyword)、標點符號(punctuator)、數值(numeric),成為一個個tokens。

這裏用Esprima編輯器作示範。

2. 抽象語法樹(AST - Abstract Syntax Tree)

根據tokens組成抽象語法樹,用樹狀去表現當中的結構和關係,並用一個個節點的形成顯示出來。

3. 直譯器(Interpreter)

直譯器會根據AST結構,產生byte code,讓機器看得懂。當byte code生成後,這時候機器已經能夠用byte code來執行程式,而之前產生的AST就會被刪掉,省回記憶體空間。

4. 性能分析(Profiler)+ 優化編譯器(Compiler)

雖然這個階段可依靠byte code來執行程式,但效能仍然是很低。因為當機器面對大量重複同一動作程式碼,例如有一個function負責把兩個數字相加,但它要重複跑100次,如果按之前的做法,以上流程便要跑100次,這樣效能會很低。

所以,這時候優化編譯器就會透過性能分析,檢視和計算有沒有重複次數很大的機器碼,如有的話,它便會假設你之後都會想重複這個動作,於是幫你優化成更精簡的機器碼並且儲存。

優化前的假設不成立?

如果發現優化程式碼的假設不成立,引擎會返回直譯器,產生新的byte code。
由於JavaScript容許隨時更改型別和值,因此之前在優化前,即是性能分析及優化編譯器過程中,所定立的假設就會不成立,這時候便要重返直譯器產生新的byte code。

小總結

現時不少開發者認為不能把JavaScript單純介定為直譯型語言,而是要根據JavaScript如何被執行而定,也有人認為JavaScript是結合直譯與編譯的特性。原因是早期首個JavaScript引擎的確只有直譯器,但因為效能低,所以促成結合編譯程序來改善效能。

例如Chrome的V8引擎,已經引入了編譯的程序提升效能。在JIT(Just in time compiler)編譯過程中,記錄及監視有否重複執行的程式碼,檢查它們到底重複跑了多少次,如果重複次數太多的話,就會幫你優化成更快的機器碼,令執行速度更快。

總結

這篇內容比較像是JavaScript的背景簡介與額外冷知識。明天就會正式進入JavaScript的主要課題了~

參考資料

javascript.info
eloquent javascript
JavaScript 入門篇- 學徒的試煉 (六角學院課程)

JavaScript引擎部分:
JS 原力覺醒 Day02 - JavaScript V8 引擎
前端开发JavaScript原理:引擎基础
[译] 揭开 JavaScript 引擎的面纱
JavaScript Visualized: the JavaScript Engine
A crash course in just-in-time (JIT) compilers


上一篇
JavaScript基本功修練: Day1 - 前言
下一篇
JavaScript基本功修練:Day3 - 基本型別(I) - 數字
系列文
JavaScript基本功修煉31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
ChungKaiLu
iT邦新手 5 級 ‧ 2021-12-28 08:36:22

謝謝~很棒的文章

我要留言

立即登入留言