亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

深入理解 JavaScript Errors 和 Stack Traces

raledong / 2056人閱讀

摘要:當(dāng)執(zhí)行完畢后也會(huì)從棧頂移出,控制流交還到。一個(gè)的堆棧追蹤包含了從其構(gòu)造函數(shù)開(kāi)始的所有堆棧幀。我們將捕獲當(dāng)前堆棧路徑并且將其存儲(chǔ)到一個(gè)普通對(duì)象中。用表示起始堆棧函數(shù)指示器這個(gè)名字記錄。

譯者注:本文作者是著名 JavaScript BDD 測(cè)試框架 Chai.js 源碼貢獻(xiàn)者之一,Chai.js 中會(huì)遇到很多異常處理的情況。跟隨作者思路,從 JavaScript 基本的 Errors 原理,到如何實(shí)際使用 Stack Traces,深入學(xué)習(xí)和理解 JavaScript Errors 和 Stack Traces。文章貼出的源碼鏈接也非常值得學(xué)習(xí)。

作者:lucasfcosta

編譯:胡子大哈

翻譯原文:[http://huziketang.com/blog/po...
](http://huziketang.com/blog/po...

英文原文:JavaScript Errors and Stack Traces in Depth

轉(zhuǎn)載請(qǐng)注明出處,保留原文鏈接以及作者信息

很久沒(méi)給大家更新關(guān)于 JavaScript 的內(nèi)容了,這篇文章我們來(lái)聊聊 JavaScript 。

這次我們聊聊 Errors 和 Stack traces 以及如何熟練地使用它們。

很多同學(xué)并不重視這些細(xì)節(jié),但是這些知識(shí)在你寫(xiě) Testing 和 Error 相關(guān)的 lib 的時(shí)候是非常有用的。使用 Stack traces 可以清理無(wú)用的數(shù)據(jù),讓你關(guān)注真正重要的問(wèn)題。同時(shí),你真正理解 Errors 和它們的屬性到底是什么的時(shí)候,你將會(huì)更有信心的使用它們。

這篇文章在開(kāi)始的時(shí)候看起來(lái)比較簡(jiǎn)單,但當(dāng)你熟練運(yùn)用 Stack trace 以后則會(huì)感到非常復(fù)雜。所以在看難的章節(jié)之前,請(qǐng)確保你理解了前面的內(nèi)容。

Stack是如何工作的

在我們談到 Errors 之前,我們必須理解 Stack 是如何工作的。它其實(shí)非常簡(jiǎn)單,但是在開(kāi)始之前了解它也是非常必要的。如果你已經(jīng)知道了這些,可以略過(guò)這一章節(jié)。

每當(dāng)有一個(gè)函數(shù)調(diào)用,就會(huì)將其壓入棧頂。在調(diào)用結(jié)束的時(shí)候再將其從棧頂移出。

這種有趣的數(shù)據(jù)結(jié)構(gòu)叫做“最后一個(gè)進(jìn)入的,將會(huì)第一個(gè)出去”。這就是廣為所知的 LIFO(后進(jìn)先出)。

舉個(gè)例子,在函數(shù) x 的內(nèi)部調(diào)用了函數(shù) y,這時(shí)棧中就有個(gè)順序先 x 后 y。我再舉另外一個(gè)例子,看下面代碼:

function c() {
    console.log("c");
}

function b() {
    console.log("b");
    c();
}

function a() {
    console.log("a");
    b();
}

a();

上面的這段代碼,當(dāng)運(yùn)行 a 的時(shí)候,它會(huì)被壓到棧頂。然后,當(dāng) b 在 a 中被調(diào)用的時(shí)候,它會(huì)被繼續(xù)壓入棧頂,當(dāng) c 在 b 中被調(diào)用的時(shí)候,也一樣。

在運(yùn)行 c 的時(shí)候,棧中包含了 a,b,c,并且其順序也是 a,b,c。

當(dāng) c 調(diào)用完畢時(shí),它會(huì)被從棧頂移出,隨后控制流回到 b。當(dāng) b 執(zhí)行完畢后也會(huì)從棧頂移出,控制流交還到 a。最后,當(dāng) a 執(zhí)行完畢后也會(huì)從棧中移出。

為了更好的展示這樣一種行為,我們用console.trace()來(lái)將 Stack trace 打印到控制臺(tái)上來(lái)。通常我們讀 Stack traces 信息的時(shí)候是從上往下讀的。

function c() {
    console.log("c");
    console.trace();
}

function b() {
    console.log("b");
    c();
}

function a() {
    console.log("a");
    b();
}

a();

當(dāng)我們?cè)贜ode REPL服務(wù)端執(zhí)行的時(shí)候,會(huì)返回如下:

Trace
    at c (repl:3:9)
    at b (repl:3:1)
    at a (repl:3:1)
    at repl:1:1 // <-- For now feel free to ignore anything below this point, these are Node"s internals
    at realRunInThisContextScript (vm.js:22:35)
    at sigintHandlersWrap (vm.js:98:12)
    at ContextifyScript.Script.runInThisContext (vm.js:24:12)
    at REPLServer.defaultEval (repl.js:313:29)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)

從上面我們可以看到,當(dāng)棧信息從 c 中打印出來(lái)的時(shí)候,我看到了 a,b 和 c?,F(xiàn)在,如果在 c 執(zhí)行完畢以后,在 b 中把 Stack trace 打印出來(lái),我們可以看到 c 已經(jīng)從棧中移出了,棧中只有 a 和 b。

function c() {
    console.log("c");
}

function b() {
    console.log("b");
    c();
    console.trace();
}

function a() {
    console.log("a");
    b();
}

a();

下面可以看到,c 已經(jīng)不在棧中了,在其執(zhí)行完以后,從棧中 pop 出去了。

Trace
    at b (repl:4:9)
    at a (repl:3:1)
    at repl:1:1  // <-- For now feel free to ignore anything below this point, these are Node"s internals
    at realRunInThisContextScript (vm.js:22:35)
    at sigintHandlersWrap (vm.js:98:12)
    at ContextifyScript.Script.runInThisContext (vm.js:24:12)
    at REPLServer.defaultEval (repl.js:313:29)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.onLine (repl.js:513:10)

概括一下:當(dāng)調(diào)用時(shí),壓入棧頂。當(dāng)它執(zhí)行完畢時(shí),被彈出棧,就是這么簡(jiǎn)單。

Error 對(duì)象和 Error 處理

當(dāng)Error發(fā)生的時(shí)候,通常會(huì)拋出一個(gè)Error對(duì)象。Error對(duì)象也可以被看做一個(gè)Error原型,用戶可以擴(kuò)展其含義,以創(chuàng)建自己的 Error 對(duì)象。

Error.prototype對(duì)象通常包含下面屬性:

constructor - 一個(gè)錯(cuò)誤實(shí)例原型的構(gòu)造函數(shù)

message - 錯(cuò)誤信息

name - 錯(cuò)誤名稱

這幾個(gè)都是標(biāo)準(zhǔn)屬性,有時(shí)不同編譯的環(huán)境會(huì)有其獨(dú)特的屬性。在一些環(huán)境中,例如 Node 和 Firefox,甚至還有stack屬性,這里面包含了錯(cuò)誤的 Stack trace。一個(gè)Error的堆棧追蹤包含了從其構(gòu)造函數(shù)開(kāi)始的所有堆棧幀

如果你想要學(xué)習(xí)一個(gè)Error對(duì)象的特殊屬性,我強(qiáng)烈建議你看一下在MDN上的這篇文章。

要拋出一個(gè)Error,你必須使用throw關(guān)鍵字。為了catch一個(gè)拋出的Error,你必須把可能拋出Error的代碼用try塊包起來(lái)。然后緊跟著一個(gè)catch塊,catch塊中通常會(huì)接受一個(gè)包含了錯(cuò)誤信息的參數(shù)。

和在 Java 中類似,不論在try中是否拋出Error, JavaScript 中都允許你在try/catch塊后面緊跟著一個(gè)finally塊。不論你在try中的操作是否生效,在你操作完以后,都用finally來(lái)清理對(duì)象,這是個(gè)編程的好習(xí)慣。

介紹到現(xiàn)在的知識(shí),可能對(duì)于大部分人來(lái)說(shuō),都是已經(jīng)掌握了的,那么現(xiàn)在我們就進(jìn)行更深入一些的吧。

使用try塊時(shí),后面可以不跟著catch塊,但是必須跟著finally塊。所以我們就有三種不同形式的try語(yǔ)句:

try...catch

try...finally

try...catch...finally

Try語(yǔ)句也可以內(nèi)嵌在一個(gè)try語(yǔ)句中,如:

try {
    try {
        // 這里拋出的Error,將被下面的catch獲取到
        throw new Error("Nested error."); 
    } catch (nestedErr) {
        // 這里會(huì)打印出來(lái)
        console.log("Nested catch");
    }
} catch (err) {
    console.log("This will not run.");
}

你也可以把try語(yǔ)句內(nèi)嵌在catchfinally塊中:

try {
    throw new Error("First error");
} catch (err) {
    console.log("First catch running");
    try {
        throw new Error("Second error");
    } catch (nestedErr) {
        console.log("Second catch running.");
    }
}

*

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.");
}

}

這里給出另外一個(gè)重要的提示:你可以拋出非Error對(duì)象的值。盡管這看起來(lái)很炫酷,很靈活,但實(shí)際上這個(gè)用法并不好,尤其在一個(gè)開(kāi)發(fā)者改另一個(gè)開(kāi)發(fā)者寫(xiě)的庫(kù)的時(shí)候。因?yàn)檫@樣代碼沒(méi)有一個(gè)標(biāo)準(zhǔn),你不知道其他人會(huì)拋出什么信息。這樣的話,你就不能簡(jiǎn)單的相信拋出的Error信息了,因?yàn)橛锌赡芩⒉皇?b>Error信息,而是一個(gè)字符串或者一個(gè)數(shù)字。另外這也導(dǎo)致了如果你需要處理 Stack trace 或者其他有意義的元數(shù)據(jù),也將變的很困難。

例如給你下面這段代碼:

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 funcThatThrowsError() {
    throw new TypeError("I am a TypeError.");
}

runWithoutThrowing(funcThatThrowsError);

這段代碼,如果其他人傳遞一個(gè)帶有拋出Error對(duì)象的函數(shù)給runWithoutThrowing函數(shù)的話,將完美運(yùn)行。然而,如果他拋出一個(gè)String類型的話,則情況就麻煩了。

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);

可以看到這段代碼中,第二個(gè)console.log會(huì)告訴你這個(gè) Error 信息是undefined。這現(xiàn)在看起來(lái)不是很重要,但是如果你需要確定是否這個(gè)Error中確實(shí)包含某個(gè)屬性,或者用另一種方式處理Error的特殊屬性,那你就需要多花很多的功夫了。

另外,當(dāng)拋出一個(gè)非Error對(duì)象的值時(shí),你沒(méi)有訪問(wèn)Error對(duì)象的一些重要的數(shù)據(jù),比如它的堆棧,而這在一些編譯環(huán)境中是一個(gè)非常重要的Error對(duì)象屬性。

Error 還可以當(dāng)做其他普通對(duì)象一樣使用,你并不需要拋出它。這就是為什么它通常作為回調(diào)函數(shù)的第一個(gè)參數(shù),就像fs.readdir函數(shù)這樣:

const fs = require("fs");

fs.readdir("/example/i-do-not-exist", function callback(err, dirs) {
    if (err instanceof Error) {
        // "readdir"將會(huì)拋出一個(gè)異常,因?yàn)槟夸洸淮嬖?        // 我們可以在我們的回調(diào)函數(shù)中使用 Error 對(duì)象
        console.log("Error Message: " + err.message);
        console.log("See? We can use  Errors  without using try statements.");
    } else {
        console.log(dirs);
    }
});

最后,你也可以在 promise 被 reject 的時(shí)候使用Error對(duì)象,這使得處理 promise reject 變得很簡(jiǎn)單。

new Promise(function(resolve, reject) {
    reject(new Error("The promise was rejected."));
}).then(function() {
    console.log("I am an error.");
}).catch(function(err) {
    if (err instanceof Error) {
        console.log("The promise was rejected with an error.");
        console.log("Error Message: " + err.message);
    }
});
使用 Stack Trace

ok,那么現(xiàn)在,你們所期待的部分來(lái)了:如何使用堆棧追蹤。

這一章專門討論支持 Error.captureStackTrace 的環(huán)境,如:NodeJS。

Error.captureStackTrace函數(shù)的第一個(gè)參數(shù)是一個(gè)object對(duì)象,第二個(gè)參數(shù)是一個(gè)可選的function。捕獲堆棧跟蹤所做的是要捕獲當(dāng)前堆棧的路徑(這是顯而易見(jiàn)的),并且在 object 對(duì)象上創(chuàng)建一個(gè)stack屬性來(lái)存儲(chǔ)它。如果提供了第二個(gè) function 參數(shù),那么這個(gè)被傳遞的函數(shù)將會(huì)被看成是本次堆棧調(diào)用的終點(diǎn),本次堆棧跟蹤只會(huì)展示到這個(gè)函數(shù)被調(diào)用之前。

我們來(lái)用幾個(gè)例子來(lái)更清晰的解釋下。我們將捕獲當(dāng)前堆棧路徑并且將其存儲(chǔ)到一個(gè)普通 object 對(duì)象中。

const myObj = {};

function c() {
}

function b() {
    // 這里存儲(chǔ)當(dāng)前的堆棧路徑,保存到myObj中
    Error.captureStackTrace(myObj);
    c();
}

function a() {
    b();
}

// 首先調(diào)用這些函數(shù)
a();

// 這里,我們看一下堆棧路徑往 myObj.stack 中存儲(chǔ)了什么
console.log(myObj.stack);

// 這里將會(huì)打印如下堆棧信息到控制臺(tái)
//    at b (repl:3:7) <-- Since it was called inside B, the B call is the last entry in the stack
//    at a (repl:2:1)
//    at repl:1:1 <-- Node internals below this line
//    at realRunInThisContextScript (vm.js:22:35)
//    at sigintHandlersWrap (vm.js:98:12)
//    at ContextifyScript.Script.runInThisContext (vm.js:24:12)
//    at REPLServer.defaultEval (repl.js:313:29)
//    at bound (domain.js:280:14)
//    at REPLServer.runBound [as eval] (domain.js:293:12)
//    at REPLServer.onLine (repl.js:513:10)

我們從上面的例子中可以看到,我們首先調(diào)用了a(a被壓入棧),然后從a的內(nèi)部調(diào)用了b(b被壓入棧,并且在a的上面)。在b中,我們捕獲到了當(dāng)前堆棧路徑并且將其存儲(chǔ)在了myObj中。這就是為什么打印在控制臺(tái)上的只有ab,而且是下面a上面b。

好的,那么現(xiàn)在,我們傳遞第二個(gè)參數(shù)到Error.captureStackTrace看看會(huì)發(fā)生什么?

const myObj = {};

function d() {
    // 這里存儲(chǔ)當(dāng)前的堆棧路徑,保存到myObj中
    // 這次我們隱藏包含b在內(nèi)的b以后的所有堆棧幀
    Error.captureStackTrace(myObj, b);
}

function c() {
    d();
}

function b() {
    c();
}

function a() {
    b();
}

// 首先調(diào)用這些函數(shù)
a();

// 這里,我們看一下堆棧路徑往 myObj.stack 中存儲(chǔ)了什么
console.log(myObj.stack);

// 這里將會(huì)打印如下堆棧信息到控制臺(tái)
//    at a (repl:2:1) <-- As you can see here we only get frames before `b` was called
//    at repl:1:1 <-- Node internals below this line
//    at realRunInThisContextScript (vm.js:22:35)
//    at sigintHandlersWrap (vm.js:98:12)
//    at ContextifyScript.Script.runInThisContext (vm.js:24:12)
//    at REPLServer.defaultEval (repl.js:313:29)
//    at bound (domain.js:280:14)
//    at REPLServer.runBound [as eval] (domain.js:293:12)
//    at REPLServer.onLine (repl.js:513:10)
//    at emitOne (events.js:101:20)

當(dāng)我們傳遞bError.captureStackTraceFunction里時(shí),它隱藏了b和在它以上的所有堆棧幀。這就是為什么堆棧路徑里只有a的原因。

看到這,你可能會(huì)問(wèn)這樣一個(gè)問(wèn)題:“為什么這是有用的呢?”。它之所以有用,是因?yàn)槟憧梢噪[藏所有的內(nèi)部實(shí)現(xiàn)細(xì)節(jié),而這些細(xì)節(jié)其他開(kāi)發(fā)者調(diào)用的時(shí)候并不需要知道。例如,在 Chai 中,我們用這種方法對(duì)我們代碼的調(diào)用者屏蔽了不相關(guān)的實(shí)現(xiàn)細(xì)節(jié)。

真實(shí)場(chǎng)景中的 Stack Trace 處理

正如我在上一節(jié)中提到的,Chai 用棧處理技術(shù)使得堆棧路徑和調(diào)用者更加相關(guān),這里是我們?nèi)绾螌?shí)現(xiàn)它的。

首先,讓我們來(lái)看一下當(dāng)一個(gè) Assertion 失敗的時(shí)候,AssertionError的構(gòu)造函數(shù)做了什么。

// "ssfi"代表"起始堆棧函數(shù)",它是移除其他不相關(guān)堆棧幀的起始標(biāo)記
function AssertionError (message, _props, ssf) {
  var extend = exclude("name", "message", "stack", "constructor", "toJSON")
    , props = extend(_props || {});

  // 默認(rèn)值
  this.message = message || "Unspecified AssertionError";
  this.showDiff = false;

  // 從屬性中copy
  for (var key in props) {
    this[key] = props[key];
  }

  // 這里是和我們相關(guān)的
  // 如果提供了起始堆棧函數(shù),那么我們從當(dāng)前堆棧路徑中獲取到,
  // 并且將其傳遞給"captureStackTrace",以保證移除其后的所有幀
  ssf = ssf || arguments.callee;
  if (ssf && Error.captureStackTrace) {
    Error.captureStackTrace(this, ssf);
  } else {
    // 如果沒(méi)有提供起始堆棧函數(shù),那么使用原始堆棧
    try {
      throw new Error();
    } catch(e) {
      this.stack = e.stack;
    }
  }
}

正如你在上面可以看到的,我們使用了Error.captureStackTrace來(lái)捕獲堆棧路徑,并且把它存儲(chǔ)在我們所創(chuàng)建的一個(gè)AssertionError實(shí)例中。然后傳遞了一個(gè)起始堆棧函數(shù)進(jìn)去(用if判斷如果存在則傳遞),這樣就從堆棧路徑中移除掉了不相關(guān)的堆棧幀,不顯示一些內(nèi)部實(shí)現(xiàn)細(xì)節(jié),保證了堆棧信息的“清潔”。

感興趣的讀者可以繼續(xù)看一下最近 @meeber 在 這里 的代碼。

在我們繼續(xù)看下面的代碼之前,我要先告訴你addChainableMethod都做了什么。它添加所傳遞的可以被鏈?zhǔn)秸{(diào)用的方法到 Assertion,并且用包含了 Assertion 的方法標(biāo)記 Assertion 本身。用ssfi(表示起始堆棧函數(shù)指示器)這個(gè)名字記錄。這意味著當(dāng)前 Assertion 就是堆棧的最后一幀,就是說(shuō)不會(huì)再多顯示任何 Chai 項(xiàng)目中的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)了。我在這里就不多列出來(lái)其整個(gè)代碼了,里面用了很多 trick 的方法,但是如果你想了解更多,可以從 這個(gè)鏈接 里獲取到。

在下面的代碼中,展示了lengthOf的 Assertion 的邏輯,它是用來(lái)檢查一個(gè)對(duì)象的確定長(zhǎng)度的。我們希望調(diào)用我們函數(shù)的開(kāi)發(fā)者這樣來(lái)使用:expect(["foo", "bar"]).to.have.lengthOf(2)。

function assertLength (n, msg) {
    if (msg) flag(this, "message", msg);
    var obj = flag(this, "object")
        , ssfi = flag(this, "ssfi");

    // 密切關(guān)注這一行
    new Assertion(obj, msg, ssfi, true).to.have.property("length");
    var len = obj.length;

    // 這一行也是相關(guān)的
    this.assert(
            len == n
        , "expected #{this} to have a length of #{exp} but got #{act}"
        , "expected #{this} to not have a length of #{act}"
        , n
        , len
    );
}

Assertion.addChainableMethod("lengthOf", assertLength, assertLengthChain);

在代碼中,我著重對(duì)跟我們相關(guān)的代碼進(jìn)行了注釋,我們從this.assert的調(diào)用開(kāi)始。

下面是this.assert方法的代碼:

Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) {
    var ok = util.test(this, arguments);
    if (false !== showDiff) showDiff = true;
    if (undefined === expected && undefined === _actual) showDiff = false;
    if (true !== config.showDiff) showDiff = false;

    if (!ok) {
        msg = util.getMessage(this, arguments);
        var actual = util.getActual(this, arguments);

        // 這是和我們相關(guān)的行
        throw new AssertionError(msg, {
                actual: actual
            , expected: expected
            , showDiff: showDiff
        }, (config.includeStack) ? this.assert : flag(this, "ssfi"));
    }
};

assert方法主要用來(lái)檢查 Assertion 的布爾表達(dá)式是真還是假。如果是假,則我們必須實(shí)例化一個(gè)AssertionError。這里注意,當(dāng)我們實(shí)例化一個(gè)AssertionError對(duì)象的時(shí)候,我們也傳遞了一個(gè)起始堆棧函數(shù)指示器(ssfi)。如果配置標(biāo)記includeStack是打開(kāi)的,我們通過(guò)傳遞一個(gè)this.assert給調(diào)用者,以向他展示整個(gè)堆棧路徑。可是,如果includeStack配置是關(guān)閉的,我們則必須從堆棧路徑中隱藏內(nèi)部實(shí)現(xiàn)細(xì)節(jié),這就需要用到存儲(chǔ)在ssfi中的標(biāo)記了。

ok,那么我們?cè)賮?lái)討論一下其他和我們相關(guān)的代碼:

new Assertion(obj, msg, ssfi, true).to.have.property("length");

可以看到,當(dāng)創(chuàng)建這個(gè)內(nèi)嵌 Assertion 的時(shí)候,我們傳遞了ssfi中已獲取到的內(nèi)容。這意味著,當(dāng)創(chuàng)建一個(gè)新的 Assertion 時(shí),將使用這個(gè)函數(shù)來(lái)作為從堆棧路徑中移除無(wú)用堆棧幀的起始點(diǎn)。順便說(shuō)一下,下面這段代碼是Assertion的構(gòu)造函數(shù)。

function Assertion (obj, msg, ssfi, lockSsfi) {
    // 這是和我們相關(guān)的行
    flag(this, "ssfi", ssfi || Assertion);
    flag(this, "lockSsfi", lockSsfi);
    flag(this, "object", obj);
    flag(this, "message", msg);

    return util.proxify(this);
}

還記得我在講述addChainableMethod時(shí)說(shuō)的,它用包含他自己的方法設(shè)置的ssfi標(biāo)記,這就意味著這是堆棧路徑中最底層的內(nèi)部幀,我們可以移除在它之上的所有幀。

回想上面的代碼,內(nèi)嵌 Assertion 用來(lái)判斷對(duì)象是不是有合適的長(zhǎng)度(Length)。傳遞ssfi到這個(gè) Assertion 中,要避免重置我們要將其作為起始指示器的堆棧幀,并且使先前的addChainableMethod在堆棧中保持可見(jiàn)狀態(tài)。

這看起來(lái)可能有點(diǎn)復(fù)雜,現(xiàn)在我們重新回顧一下,我們想要移除沒(méi)有用的堆棧幀都做了什么工作:

當(dāng)我們運(yùn)行一個(gè) Assertion 時(shí),我們?cè)O(shè)置它本身來(lái)作為我們移除其后面堆棧幀的標(biāo)記。

這個(gè) Assertion 開(kāi)始執(zhí)行,如果判斷失敗,那么從剛才我們所存儲(chǔ)的那個(gè)標(biāo)記開(kāi)始,移除其后面所有的內(nèi)部幀。

如果有內(nèi)嵌 Assertion,那么我們必須要使用包含當(dāng)前 Assertion 的方法作為移除后面堆棧幀的標(biāo)記,即放到ssfi中。因此我們要傳遞當(dāng)前ssfi(起始堆棧函數(shù)指示器)到我們即將要新創(chuàng)建的內(nèi)嵌 Assertion 中來(lái)存儲(chǔ)起來(lái)。

最后我還是強(qiáng)烈建議來(lái)閱讀一下 @meeber的評(píng)論 來(lái)加深對(duì)它的理解。

我最近正在寫(xiě)一本《React.js 小書(shū)》,對(duì) React.js 感興趣的童鞋,歡迎指點(diǎn)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/81597.html

相關(guān)文章

  • 前端空間 - 收藏集 - 掘金

    摘要:封裝手寫(xiě)的方筆記使用檢測(cè)文件前端掘金副標(biāo)題可以做什么以及使用中會(huì)遇到的坑。目的是幫助人們用純中文指南實(shí)現(xiàn)復(fù)選框中多選功能前端掘金作者緝熙簡(jiǎn)介是推出的一個(gè)天挑戰(zhàn)。 深入理解 JavaScript Errors 和 Stack Traces - 前端 - 掘金譯者注:本文作者是著名 JavaScript BDD 測(cè)試框架 Chai.js 源碼貢獻(xiàn)者之一,Chai.js 中會(huì)遇到很多異常處理...

    you_De 評(píng)論0 收藏0
  • 前端空間 - 收藏集 - 掘金

    摘要:封裝手寫(xiě)的方筆記使用檢測(cè)文件前端掘金副標(biāo)題可以做什么以及使用中會(huì)遇到的坑。目的是幫助人們用純中文指南實(shí)現(xiàn)復(fù)選框中多選功能前端掘金作者緝熙簡(jiǎn)介是推出的一個(gè)天挑戰(zhàn)。 深入理解 JavaScript Errors 和 Stack Traces - 前端 - 掘金譯者注:本文作者是著名 JavaScript BDD 測(cè)試框架 Chai.js 源碼貢獻(xiàn)者之一,Chai.js 中會(huì)遇到很多異常處理...

    lwx12525 評(píng)論0 收藏0
  • 翻譯-JavaScript異常調(diào)用堆棧的處理

    摘要:調(diào)用堆棧實(shí)際上就是一個(gè)方法列表,按調(diào)用順序保存所有在運(yùn)行期被調(diào)用的方法。調(diào)用堆棧會(huì)將當(dāng)前正在執(zhí)行的函數(shù)調(diào)用壓入堆棧,一旦函數(shù)調(diào)用結(jié)束,又會(huì)將它移出堆棧。 原文 JavaScript Errors and Stack Traces in Depth 調(diào)用棧Call Stack是如何工作的 棧是一個(gè)后進(jìn)先出LIFO (Last in,F(xiàn)irst out)的數(shù)據(jù)結(jié)構(gòu)。調(diào)用堆棧實(shí)際上就是一個(gè)方...

    adam1q84 評(píng)論0 收藏0
  • javasctipt 工作原理之調(diào)用棧

    摘要:譯者注翻譯一個(gè)對(duì)新手比較友好的工作原理解析系列文章注意以下全部是概念經(jīng)驗(yàn)豐富的老鳥(niǎo)可以離場(chǎng)啦正文從這里開(kāi)始隨著的流行團(tuán)隊(duì)們正在利用來(lái)支持多個(gè)級(jí)別的技術(shù)棧包括前端后端混合開(kāi)發(fā)嵌入式設(shè)備以及更多這篇文章旨在成為深入挖掘和實(shí)際上他是怎么工作的系列 譯者注 翻譯一個(gè)對(duì)新手比較友好的 JavaScript 工作原理解析系列文章 注意: 以下全部是概念,經(jīng)驗(yàn)豐富的老鳥(niǎo)可以離場(chǎng)啦 正文從這里開(kāi)始 隨...

    Pines_Cheng 評(píng)論0 收藏0
  • 一種監(jiān)聽(tīng)ANR方案

    摘要:監(jiān)聽(tīng)上報(bào)應(yīng)用無(wú)響應(yīng)是數(shù)據(jù)采集系統(tǒng)功能之一,本文講述一種可行實(shí)現(xiàn)方案。是一個(gè)用于監(jiān)聽(tīng)文件訪問(wèn)創(chuàng)建修改刪除移動(dòng)等操作的監(jiān)聽(tīng)器。為此本文同時(shí)提供一種線程輪詢措施,用于輔助監(jiān)聽(tīng)。 監(jiān)聽(tīng)上報(bào)ANR(Application Not Responding,應(yīng)用無(wú)響應(yīng))是數(shù)據(jù)采集系統(tǒng)功能之一,本文講述一種可行實(shí)現(xiàn)方案。 方案概述 ANR一般有三種類型[1]: KeyDispatchTimeout(5...

    winterdawn 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

raledong

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<