JavaScript運行機制範例程式碼分析
JavaScript執行機制範例程式碼分析
#從一個簡單的問題談起:
<script type="text/javascript"> alert(i); var i = 1; </script>
輸出結果是undefined, 這種現像被稱為「預解析」:JavaScript引擎會優先解析var變數和function定義。在預解析完成後,才會執行程式碼。如果一個文件流程包含多個script程式碼段(用script標籤分隔的js程式碼或引入的js檔案).
運行順序是:
step1. 讀入第一個程式碼段
step2. 做語法分析,有錯誤則報語法錯誤(例如括號不符等),並跳到step5
step3. 對var變數和function定義做「預解析」(永遠不會報錯的,因為只解析正確的宣告)
step4. 執行程式碼段,有錯則報錯(例如變數未定義)
step5. 如果還有下一個程式碼段,則讀入下一個程式碼段,重複step2
step6. 結束
上面的分析,已經能解釋很多問題了,但老覺得欠缺點什麼。例如step3裡,「預解析」究竟是怎麼回事?還有step4裡,看下面的例子:
<script type="text/javascript"> alert(i); // error: i is not defined. i = 1; </script>
為什麼第一句會導致錯誤? JavaScript中,變數不是可以不定義嗎?
編譯過程
時間如白馬過隙,書櫃旁翻開恍如隔世般的《編譯原理》,熟悉而又陌生的空白處有著這樣的筆記:
對於傳統編譯型語言來說,編譯步驟分為:詞法分析、語法分析、語意檢查、程式碼最佳化和位元組生成。
但對於解釋型語言來說,透過詞法分析和語法分析得到語法樹後,就可以開始解釋執行了。
簡單地說,詞法分析是將字元流(char stream)轉換為記號流(token stream), 例如將c = a – b;轉換為:
NAME "c" EQUALS NAME "a" MINUS NAME "b" SEMICOLON
上面只是範例,更進一步的了解請查看Lexical Analysis.
《JavaScript權威指南》的第2章,講的就是詞法結構(Lexical Structure),ECMA-262 中也有描述。詞法結構是一門語言的基礎,很容易掌握。至於詞法分析的實現那是另一個研究領域,在此不探究。
可以拿自然語言來類比,詞法分析是一對一的硬性翻譯,例如一段英文,逐詞翻譯成中文,得到的是一堆記號流,還很難理解。進一步的翻譯,就需要語法分析了,下圖是一個條件語句的語法樹:
構造語法樹的時候,如果發現無法構造,比如if(a { i = 2; }, 就會報語法錯誤,並結束整個程式碼區塊的解析,這就是本文開頭部分的step2.
透過語法分析,建構出語法樹後,翻譯出來的句子可能還會有模糊的地方,接下來還需要進一步的語意檢查。精力有限,沒時間去看JS的引擎實現,不敢確定JS引擎中是否有語意檢查這一步)。和語法分析,之後可能還有語意檢查、程式碼最佳化等步驟,等這些編譯步驟完成之後(任何語言都有編譯過程,只是解釋型語言沒有編譯成二進位程式碼),才會開始執行程式碼。
上面的編譯過程,還是無法更深入的解釋文章開頭部分的“預解析”,我們還得仔細探究下JavaScript程式碼的執行過程。愛民在《JavaScript語言精髓與程式設計實踐》的第二部分,對此有非常仔細的分析。 以下是我的一些領悟:透過編譯,JavaScript程式碼已經翻譯成了語法樹,然後會立刻依照語法樹執行。時決定而不是執行時決定,也就是說詞法作用域取決於源碼,編譯器透過靜態分析就能確定,因此詞法作用域也叫做靜態作用域(static scope)。無法僅透過靜態技術實現,實際上,只能說JS的作用域機制非常接近lexical scope.JS引擎在執行每個函數實例時,都會建立一個執行環境(execution context)。 context中包含一個呼叫物件(call object), 呼叫物件是一個scriptObject結構,用來保存內部變數表varDecls、內嵌函數表funDecls、父級引用列表upvalue等語法分析結構(注意:varDecls和funDecls等資訊是在語法分析階段就已經得到,並保存在語法樹中。函數實例執行時,會將這些資訊從語法樹複製到 scriptObject上)。 scriptObject是與函數相關的一套靜態系統,與函數實例的生命週期一致。lexical scope是JS的作用域机制,还需要理解它的实现方法,这就是作用域链(scope chain)。scope chain是一个name lookup机制,首先在当前执行环境的scriptObject中寻找,没找到,则顺着upvalue到父级scriptObject中寻找,一直 lookup到全局调用对象(global object)。
当一个函数实例执行时,会创建或关联到一个闭包(closure)。 scriptObject用来静态保存与函数相关的变量表,closure则在执行期动态保存这些变量表及其运行值。closure的生命周期有可能比函数实例长。函数实例在活动引用为空后会自动销毁,closure则要等要数据引用为空后,由JS引擎回收(有些情况下不会自动回收,就导致了内存泄漏)。
别被上面的一堆名词吓住,一旦理解了执行环境、调用对象、闭包、词法作用域、作用域链这些概念,JS语言的很多现象都能迎刃而解。
小结
至此,对于文章开头部分的疑问,可以解释得很清楚了:
step3中所谓的“预解析”,其实是在step2的语法分析阶段完成,并存储在语法树中。当执行到函数实例时,会将varDelcs和funcDecls从语法树中复制到执行环境的scriptObject上。
step4中,未定义变量意味着在scriptObject的变量表中找不到,JS引擎会沿着scriptObject的upvalue往上寻找,如果都没找到,对于写操作i = 1; 最后就会等价为 window.i = 1; 给window对象新增了一个属性。对于读操作,如果一直追溯到全局执行环境的scriptObject上都找不到,就会产生运行期错误。
理解后,雾散花开,天空一片晴朗。
最后,留个问题给大家:
<script type="text/javascript"> var arg = 1; function foo(arg) { alert(arg); var arg = 2; } foo(3); </script>
请问alert的输出是什么?
以上是JavaScript運行機制範例程式碼分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數
