摘要:調用堆棧實際上就是一個方法列表,按調用順序保存所有在運行期被調用的方法。調用堆棧會將當前正在執行的函數調用壓入堆棧,一旦函數調用結束,又會將它移出堆棧。
原文
JavaScript Errors and Stack Traces in Depth
調用棧Call Stack是如何工作的棧是一個后進先出LIFO (Last in,First out)的數據結構。調用堆棧實際上就是一個方法列表,按調用順序保存所有在運行期被調用的方法。調用堆棧會將當前正在執行的函數調用壓入堆棧,一旦函數調用結束,又會將它移出堆棧。
用console.trace()編寫一個簡單的例子來演示一下
function c() { console.log("c"); console.trace(); } function b() { console.log("b"); c(); } function a() { console.log("a"); b(); } a();
我們可以看到console輸出的結果
console.trace c @ VM59:3 b @ VM59:7 a @ VM59:11 (anonymous) @ VM59:13
我們調用console.trace()是在c方法里,這個時候c還在執行,并沒有返回,因此從console就能看到調用堆棧頂就是c。我們可以稍微改變一下,比如把console.trace()放在b方法里調用,如下:
function c() { console.log("c"); } function b() { console.log("b"); c(); console.trace(); } function a() { console.log("a"); b(); } a();
這時候我們再觀察console,就看不到c方法了
VM61:8 console.trace b @ VM61:8 a @ VM61:13 (anonymous) @ VM61:16
因為console.trace()的調用是發生在了c調用之后,因此這個時候,棧頂c的幀已經出棧,自然就看不到了。
Error對象以及異常處理通常程序有異常的時候都會有一個Error對象拋出。Error.prototype有以下幾種標準屬性:
constructor
message
name
更多的,可以翻翻MDN的文檔。其中有一個stack屬性,要重點關注。盡管這是一個非標準屬性,但是絕大多數瀏覽器都支持這個屬性。
一般我們使用try/catch來捕獲異常,同時,我們還可以使用finally來做一些清理的工作,因為finally里的代碼是一定會執行的。
try { console.log("The try block is running..."); } finally { try { throw new Error("Error inside finally."); } catch (err) { console.log("Caught an error inside the finally block."); } }
有一個值得探討的地方,那就是,你可以throw任何數據而不僅僅是一個Error類的實例。
function runWithoutThrowing(func) { try { func(); } catch (e) { console.log("There was an error, but I will not throw it."); console.log("The error"s message was: " + e.message) } } function funcThatThrowsString() { throw "I am a String."; } runWithoutThrowing(funcThatThrowsString);
這種情況下,e.message的值一定就是undefined了,因為你拋出的并不是一個Error類的實例。
異常還可以作為第一個參數傳給callback函數。舉個fs.readdir的例子,
const fs = require("fs"); fs.readdir("/example/i-do-not-exist", function callback(err, dirs) { if (err instanceof Error) { // `readdir` will throw an error because that directory does not exist // We will now be able to use the error object passed by it in our callback function console.log("Error Message: " + err.message); console.log("See? We can use Errors without using try statements."); } else { console.log(dirs); } });處理調用堆棧
思路就兩種:
Error.captureStackTrace(NodeJS)
Error.prototype.stack
Error.captureStackTrace是NodeJS提供的一個方法,這個方法會捕捉當前的調用堆棧,然后保存到你指定的對象。
const myObj = {}; function c() { } function b() { // Here we will store the current stack trace into myObj Error.captureStackTrace(myObj); c(); } function a() { b(); } // First we will call these functions a(); // Now let"s see what is the stack trace stored into myObj.stack console.log(myObj.stack);
另外一種就是利用Error對象的stack屬性。但里有個問題,就是你不知道在try/catch里拋出的是什么樣的值,這個值它不一定是Error類的實例。不過我們依然能夠處理,而且是非常巧妙的進行處理。比如看看Chai這個斷言庫的AssertionError類的構造函數。
// `ssfi` stands for "start stack function". It is the reference to the // starting point for removing irrelevant frames from the stack trace function AssertionError (message, _props, ssf) { var extend = exclude("name", "message", "stack", "constructor", "toJSON") , props = extend(_props || {}); // Default values this.message = message || "Unspecified AssertionError"; this.showDiff = false; // Copy from properties for (var key in props) { this[key] = props[key]; } // Here is what is relevant for us: // If a start stack function was provided we capture the current stack trace and pass // it to the `captureStackTrace` function so we can remove frames that come after it ssf = ssf || arguments.callee; if (ssf && Error.captureStackTrace) { Error.captureStackTrace(this, ssf); } else { // If no start stack function was provided we just use the original stack property try { throw new Error(); } catch(e) { this.stack = e.stack; } } }參考文檔
arguments.callee
arguments.caller
Error
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89863.html
摘要:幸運的是,使用符號創建的構造器,如果在不使用來調用,則始終會報錯,即使在非嚴格模式下也不會產生問題。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項目原文:Bugs and Errors 譯者:飛龍 協議:CC BY-NC-SA 4.0 自豪地采用谷歌翻譯 部分參考了《JavaScript 編程精解(第 2 版)》 調試的難度是開始編寫代碼的兩倍。 因此,如...
摘要:如果我們進入一個函數,我們在堆棧的頂部。看看下面的代碼當引擎開始執行此代碼時,調用堆棧將為空。之后,步驟如下調用堆棧中的每個條目稱為堆棧幀。這正是拋出異常時構造堆棧跟蹤的方式當異常發生時,它基本上是調用堆棧的狀態。 隨著JavaScript越來越受歡迎,團隊正在利用這個技術棧在多個層次- 前端,后端,混合應用程序,嵌入式設備等等提供支持。 這篇文章旨在成為系列中第一個旨在深入挖掘Jav...
摘要:譯者注翻譯一個對新手比較友好的工作原理解析系列文章注意以下全部是概念經驗豐富的老鳥可以離場啦正文從這里開始隨著的流行團隊們正在利用來支持多個級別的技術棧包括前端后端混合開發嵌入式設備以及更多這篇文章旨在成為深入挖掘和實際上他是怎么工作的系列 譯者注 翻譯一個對新手比較友好的 JavaScript 工作原理解析系列文章 注意: 以下全部是概念,經驗豐富的老鳥可以離場啦 正文從這里開始 隨...
摘要:每個函數調用都將開辟出一小塊稱為堆棧幀的內存。當第二個函數開始執行,堆棧幀增加到個。當這個函數調用結束后,它的幀會從堆棧中退出。保持堆棧幀跟蹤函數調用的狀態,并將其分派給下一個遞歸調用迭。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 關于譯者:這是一個流淌著滬江血液的純粹工程:認真,是 HTM...
摘要:單元測試會體現出以上錯誤處理程序的作用如果出現問題,錯誤處理程序就會返回。同時錯誤會展開堆棧,這對調試非常有幫助。展開堆棧處理異常的一種方式是在調用堆棧的頂部加入。確保你的錯誤處理處在相同域中,這樣會保留原始消息,堆棧和自定義錯誤對象。 JavaScript的事件驅動范式增添了豐富的語言,也是讓使用JavaScript編程變得更加多樣化。如果將瀏覽器設想為JavaScript的事件驅動...
閱讀 703·2021-11-18 10:02
閱讀 3579·2021-09-02 10:21
閱讀 1725·2021-08-27 16:16
閱讀 2058·2019-08-30 15:56
閱讀 2386·2019-08-29 16:53
閱讀 1373·2019-08-29 11:18
閱讀 2953·2019-08-26 10:33
閱讀 2642·2019-08-23 18:34