堆疊追蹤 API

V8 中拋出的所有內部錯誤都會在建立時擷取堆疊追蹤。此堆疊追蹤可透過非標準的 error.stack 屬性從 JavaScript 存取。V8 也有各種掛鉤,用於控制如何收集和格式化堆疊追蹤,以及允許自訂錯誤也收集堆疊追蹤。本文件概述 V8 的 JavaScript 堆疊追蹤 API。

基本堆疊追蹤 #

預設情況下,V8 拋出的幾乎所有錯誤都有 stack 屬性,其中包含格式化為字串的最上方 10 個堆疊框架。以下是完全格式化堆疊追蹤的範例

ReferenceError: FAIL is not defined
   at Constraint.execute (deltablue.js:525:2)
   at Constraint.recalculate (deltablue.js:424:21)
   at Planner.addPropagate (deltablue.js:701:6)
   at Constraint.satisfy (deltablue.js:184:15)
   at Planner.incrementalAdd (deltablue.js:591:21)
   at Constraint.addConstraint (deltablue.js:162:10)
   at Constraint.BinaryConstraint (deltablue.js:346:7)
   at Constraint.EqualityConstraint (deltablue.js:515:38)
   at chainTest (deltablue.js:807:6)
   at deltaBlue (deltablue.js:879:2)

堆疊追蹤會在建立錯誤時收集,而且無論錯誤在哪裡或拋出幾次,堆疊追蹤都相同。我們收集 10 個框架,因為這通常足夠有用,但不會多到對效能有明顯的負面影響。您可以透過設定變數來控制要收集多少個堆疊框架

Error.stackTraceLimit

將其設定為 0 會停用堆疊追蹤收集。任何有限整數值都可以用作要收集的最大框架數。將其設定為 Infinity 表示會收集所有框架。此變數只會影響目前的內容;必須為需要不同值的每個內容明確設定。 (請注意,在 V8 術語中稱為「內容」的東西對應到 Google Chrome 中的頁面或 <iframe>)。若要設定影響所有內容的不同預設值,請使用下列 V8 命令列旗標

--stack-trace-limit <value>

若要在執行 Google Chrome 時將此旗標傳遞給 V8,請使用

--js-flags='--stack-trace-limit <value>'

非同步堆疊追蹤 #

--async-stack-traces 旗標(自 V8 v7.3 起預設開啟)啟用新的 零成本非同步堆疊追蹤,它會使用非同步堆疊框架(即程式碼中的 await 位置)豐富 Error 實例的 stack 屬性。這些非同步框架會在 stack 字串中標示為 async

ReferenceError: FAIL is not defined
    at bar (<anonymous>)
    at async foo (<anonymous>)

撰寫本文時,此功能僅限於 await 位置、Promise.all()Promise.any(),因為在這些情況下,引擎可以重建必要的資訊,而不會產生任何額外負擔(這就是零成本的原因)。

自訂例外狀況的堆疊追蹤收集 #

用於內建錯誤的堆疊追蹤機制是使用一般堆疊追蹤收集 API 實作的,使用者腳本也可以使用此 API。函式

Error.captureStackTrace(error, constructorOpt)

會將堆疊屬性新增到指定的 error 物件,在呼叫 captureStackTrace 時產生堆疊追蹤。透過 Error.captureStackTrace 收集的堆疊追蹤會立即收集、格式化並附加到指定的 error 物件。

可選擇的 constructorOpt 參數允許您傳入函式值。收集堆疊追蹤時,會略過此函式最上層呼叫以上的所有框架,包括該呼叫。這有助於隱藏對使用者無用的實作細節。定義會擷取堆疊追蹤的自訂錯誤的常見方式如下:

function MyError() {
Error.captureStackTrace(this, MyError);
// Any other initialization goes here.
}

傳入 MyError 作為第二個引數表示不會在堆疊追蹤中顯示對 MyError 的建構函式呼叫。

自訂堆疊追蹤 #

與 Java 不同,Java 中的例外狀況堆疊追蹤是允許檢查堆疊狀態的結構化值,V8 中的堆疊屬性只會包含格式化堆疊追蹤的平面字串。這純粹只是為了與其他瀏覽器相容。不過,這並非硬編碼,而只是預設行為,使用者腳本可以覆寫它。

為了提高效率,堆疊追蹤並非在擷取時格式化,而是在第一次存取堆疊屬性時才依需求格式化。堆疊追蹤是透過呼叫

Error.prepareStackTrace(error, structuredStackTrace)

來格式化,並使用此呼叫傳回的內容作為 stack 屬性的值。如果您將不同的函式值指定給 Error.prepareStackTrace,就會使用該函式來格式化堆疊追蹤。它會傳入要為其準備堆疊追蹤的錯誤物件,以及堆疊的結構化表示。使用者堆疊追蹤格式化程式可以自由地格式化堆疊追蹤,甚至傳回非字串值。在 prepareStackTrace 完成呼叫後,保留對結構化堆疊追蹤物件的參照是安全的,因此它也是有效的傳回值。請注意,自訂 prepareStackTrace 函式只會在存取 Error 物件的堆疊屬性後才會呼叫。

結構化堆疊追蹤是一個 CallSite 物件陣列,每個物件都代表一個堆疊框架。CallSite 物件定義下列方法

預設堆疊追蹤是使用 CallSite API 建立的,因此在那裡可用的任何資訊也都可以透過這個 API 取得。

為了維護嚴格模式函數所施加的限制,具有嚴格模式函數的框架及其下方的所有框架(其呼叫者等)均無法存取其接收器和函數物件。對於這些框架,getFunction()getThis() 會傳回 undefined

相容性 #

此處描述的 API 僅限於 V8,且不受任何其他 JavaScript 實作支援。大多數實作確實提供 error.stack 屬性,但堆疊追蹤的格式可能與此處描述的格式不同。建議使用此 API 的方式為

附錄:堆疊追蹤格式 #

V8 使用的預設堆疊追蹤格式可以針對每個堆疊框架提供下列資訊

這些資訊中可能有些無法取得,而且會根據可取得多少資訊來使用不同的堆疊框架格式。如果所有上述資訊都可取得,格式化的堆疊框架會如下所示

at Type.functionName [as methodName] (location)

或者,在建構呼叫的情況下

at new functionName (location)

或者,在非同步呼叫的情況下

at async functionName (location)

如果 functionNamemethodName 僅有一個可取得,或者兩個都可取得但相同,格式為

at Type.name (location)

如果兩個都無法取得,則使用 <anonymous> 作為名稱。

Type 值是儲存在 this 的建構函數欄位中的函數名稱。在 V8 中,所有建構函數呼叫都會將此屬性設定為建構函數,因此除非在建立物件後主動變更此欄位,否則它會存放建立物件的函數名稱。如果無法取得,則會使用物件的 [[Class]] 屬性。

一個特殊情況是全域物件,其中不會顯示 Type。在這種情況下,堆疊框架會格式化為

at functionName [as methodName] (location)

位置本身有幾種可能的格式。最常見的是定義目前函數的指令碼中的檔案名稱、行號和欄號

fileName:lineNumber:columnNumber

如果目前函數是使用 eval 建立的,格式為

eval at position

…其中 position 是呼叫 eval 發生時的位置。請注意,這表示如果存在巢狀呼叫 eval,則位置可以巢狀,例如

eval at Foo.a (eval at Bar.z (myscript.js:10:3))

如果堆疊框架位於 V8 的函式庫中,則位置為

native

…如果不可用,則為

unknown location