<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JavaScript Errors 指南

          共 1311字,需瀏覽 3分鐘

           ·

          2020-11-20 07:17

          5f497c61313e19ff8e156c8541a36388.webp

          英文:mknichel 譯文:Jocs

          https://github.com/Jocs/jocs.github.io/issues/1

          在README文件中包含了這么多年我對JavaScript errors的學(xué)習(xí)和理解,包括把錯誤報告給服務(wù)器、在眾多bug中根據(jù)錯誤信息追溯產(chǎn)生錯誤的原因,這些都使得處理JavaScript 錯誤變得困難。瀏覽器廠商在處理JavaScript錯誤方面也有所改進,但是保證應(yīng)用程序能夠穩(wěn)健地處理JavaScript錯誤仍然有提升的空間。

          Introduction


          捕獲、報告、以及修改錯誤是維護和保持應(yīng)用程序健康穩(wěn)定運行的重要方面。由于Javascript代碼主要是在客戶端運行、客戶端環(huán)境又包括了各種各樣的瀏覽器。因此使得消除應(yīng)用程序中 JS 錯誤變得相對困難。關(guān)于如何報告在不同瀏覽器中引起的 JS 錯誤依然也沒有一個正式的規(guī)范。除此之外,瀏覽器在報告JS錯誤也有些bug,這些原因?qū)е铝讼龖?yīng)用程序中的JS 錯誤變得更加困難。這篇文章將會以以上問題作為出發(fā)點,分析JS錯誤的產(chǎn)生、JS錯誤包含哪些部分、怎么去捕獲一個JS錯誤。期待這篇文章能夠幫助到以后的開發(fā)者更好的處理JS錯誤、不同瀏覽器廠商能夠就JS錯誤找到一個標(biāo)準(zhǔn)的解決方案。

          JavaScript 錯誤剖析


          一個JavaScript 錯誤由 錯誤信息(error message) 和 追溯棧(stack trace) 兩個主要部分組成。錯誤信息是一個字符串用來描述代碼出了什么問題。追溯棧用來記錄JS錯誤具體出現(xiàn)在代碼中的位置。JS 錯誤可以通過兩種方式產(chǎn)生、要么是瀏覽器自身在解析JavaScript代碼時拋出錯誤,要么可以通過應(yīng)用程序代碼本身拋出錯誤。(譯者注:例如可以通過throw new Error() 拋出錯誤)

          產(chǎn)生一個JavaScript 錯誤

          當(dāng)JavaScript代碼不能夠被瀏覽器正確執(zhí)行的時候,瀏覽器就會拋出一個JS錯誤,或者應(yīng)用程序代碼本身也可以直接拋出一個JS錯誤。

          例如:

          var?a?=?3;

          a();


          在如上例子中,a 變量類型是一個數(shù)值,不能夠作為一個函數(shù)來調(diào)用執(zhí)行。瀏覽器在解析上面代碼時就會拋出如下錯誤TypeError: a is not a function 并通過追溯棧指出代碼出錯的位置。

          開發(fā)者也通常在條件語句中當(dāng)條件不滿足的前提下,拋出一個錯誤,例如:

          if?(!checkPrecondition())?{
          ??throw?new?Error("Doesn't?meet?precondition!");
          }


          在這種情況下,瀏覽器控制臺中的錯誤信息如是Error: Dosen’t meet precondition!. 這條錯誤也會包含一個追溯棧用來指示代碼錯誤的位置,通過瀏覽器拋出的錯誤或是通過應(yīng)用本身拋出的錯誤可以通過相同的處理手段來處理。

          開發(fā)者可以通過不同方式來拋出一個JavaScript 錯誤:

          • throw new Error(‘Problem description.’)

          • throw Error(‘Problem description.’) <— equivalent to the first one

          • throw ‘Problem description.’ <— bad

          • throw null <— even worse


          直接通過throw 操作符拋出一個字符串錯誤(**譯者注:上面第三種方式)或者或者拋出null 這兩種方式都是不推薦的,因為瀏覽器無法就以上兩種方式生成追溯棧,也就導(dǎo)致了無法追溯錯誤在代碼中的位置,因為推薦拋出一個Error 對象,Error對象不僅包含一個錯誤信息,同時也包含一個追溯棧這樣你就可以很容易通過追溯棧找到代碼出錯的行數(shù)了。

          Error Messages

          不同瀏覽器在就錯誤信息的格式有不同的實現(xiàn)形式,比如上面的例子,在把一個原始類型的變量當(dāng)做函數(shù)執(zhí)行的時候,不同瀏覽器都在試圖找到一個相同的方式來拋出這個錯誤,但是又沒有統(tǒng)一標(biāo)準(zhǔn),因此相同的形式也就沒有了保證,比如在Chrome和Firefox中,會使用{0} is not a function 形式來拋出錯誤信息,而IE11 會拋出Function expected 錯誤信息(IE瀏覽器甚至不會指出是哪個變量被當(dāng)做了函數(shù)調(diào)用而產(chǎn)生錯誤)

          然而,不同瀏覽器在就錯誤信息上也有可能產(chǎn)生分歧,比如當(dāng)switch 語句中有多個default 語句時,Chrome會拋出 “More than one default clause in switch statement” 而FireFox會拋出”more than one switch default”. 當(dāng)新特性加入到JavaScript語言中時,錯誤信息也應(yīng)該實時更新。當(dāng)處理容易產(chǎn)生混淆代碼導(dǎo)致的錯誤時,往往也需要使用到不同的處理手段。

          你可以通過如下地址找到不同瀏覽器廠商在處理錯誤信息上面的做法:

          • Firefox -?http://mxr.mozilla.org/mozilla1.9.1/source/js/src/js.msg

          • Chrome -?https://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/messages.js

          • Internet Explorer -?https://github.com/Microsoft/ChakraCore/blob/4e4d4f00f11b2ded23d1885e85fc26fcc96555da/lib/Parser/rterrors.h


          Browsers will produce different error messages for some exceptions.

          追溯棧格式

          追溯棧是用來描述錯誤出現(xiàn)在代碼中什么位置。追溯棧通過一系列相互關(guān)聯(lián)的幀組成,每一幀描述一行特定的代碼,追溯棧最上面的那一幀就是錯誤拋出的位置,追溯棧下面的幀就是一個函數(shù)調(diào)用棧 - 也就是瀏覽器在執(zhí)行JavaScript代碼時一步一步怎么到拋出錯誤代碼那一行的。

          一個基本的追溯棧如下:

          ??at?throwError?(http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9)
          ??at?http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3


          追溯棧中的每一幀由以下三個部分組成:一個函數(shù)名(發(fā)生錯誤的代碼不是在全局作用域中執(zhí)行),發(fā)生錯誤的腳本在網(wǎng)絡(luò)中的地址,以及發(fā)生錯誤代碼的行數(shù)和列數(shù)。

          遺憾的是,追溯棧還沒有一個標(biāo)準(zhǔn)形式,因此不同瀏覽器廠商在實現(xiàn)上也是有差異的。

          IE 11的追溯棧和Chrome 的追溯棧很相似,除了在全局作用域中的代碼上有些差異:

          ??at?throwError?(http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:3)
          ??at?Global?code?(http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3)


          Firefox 的追溯棧如下格式:

          ??throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9
          ??@http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3


          Safari 的追溯棧格式和Firefox很相似,但是仍然有些出入:

          ??throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:18
          ??global?code@http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:13


          所有的瀏覽器廠商追溯棧基本信息差不多,但是格式上有些差異:

          在上面Safari追溯棧的例子中,除了在追溯棧格式上和Chrome有差異外,發(fā)生錯誤的列數(shù)也和Chrome和Firefox不同。在不同的錯誤情境中,行數(shù)也會有所不同,比如如下代碼:

          (function?namedFunction()?{?throwError();?})();

          Chrome 會從throwError()開始計數(shù)行數(shù),而IE11會從上面代碼開始位置計算行數(shù)。這些不同瀏覽器之間在追溯棧格式上和計數(shù)上的差異也為后期解析追溯棧帶來了困難。

          通過如下網(wǎng)站 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack 了解更多關(guān)于追溯棧的問題。

          不同瀏覽器廠商在追溯棧格式以及列數(shù)上都有可能存在差異

          深入研究,瀏覽器廠商關(guān)于追溯棧還有很多細(xì)微差異,將在下面部分詳細(xì)討論。

          為匿名函數(shù)取名

          默認(rèn)情況下,匿名函數(shù)沒有名字,同時在追溯棧中要么表現(xiàn)為空字符串要么就是Anonymous function(根據(jù)不同瀏覽器會有區(qū)別)。為了提升代碼的可調(diào)試性,你應(yīng)該為所用的函數(shù)添加一個函數(shù)名,以使得其在追溯棧中出現(xiàn),而不是空字符串或者Anonymous function。最簡單的方法就是在所有的匿名函數(shù)前面加一個函數(shù)名,甚至該函數(shù)名不會在其他任何場合使用到。如下:

          setTimeout(function?nameOfTheAnonymousFunction()?{?...?},?0);

          上面代碼的改變將使得追溯棧中也發(fā)生如下改變,從

          at?http://mknichel.github.io/javascript-errors/javascript-errors.js:125:17


          變成了如下形式

          at?nameOfTheAnonymousFunction?(http://mknichel.github.io/javascript-errors/javascript-errors.js:121:31)

          上面給匿名函數(shù)添加姓名的方法可以保證函數(shù)名出現(xiàn)在追溯棧中,這樣也使得代碼更易調(diào)試,通過如下網(wǎng)站你可以了解更多關(guān)于代碼調(diào)試的信息。http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/

          將函數(shù)賦值給一個變量

          瀏覽器通常也會使用匿名函數(shù)賦值給的變量作為函數(shù)名,在追溯幀中出現(xiàn)。舉個例子:

          var?fnVariableName?=?function()?{?...?};


          瀏覽器會使用fnVariableName作為函數(shù)名在追溯棧中出現(xiàn)。

          ????at?throwError?(http://mknichel.github.io/javascript-errors/javascript-errors.js:27:9)
          ????at?fnVariableName?(http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)

          瀏覽器廠商在追溯棧上甚至還有更加細(xì)微的差異,如果一個函數(shù)被賦值給了一個變量,并且這個函數(shù)定義在另外一個函數(shù)內(nèi),幾乎所有的瀏覽器都會使用被賦值的變量作為追溯幀中的函數(shù)名,但是,F(xiàn)irefox有所不同,在Firefox中,會使用外面的函數(shù)名加上內(nèi)部的函數(shù)名(變量名)作為追溯幀中的函數(shù)名。舉個例子:

          function?throwErrorFromInnerFunctionAssignedToVariable()?{
          ??var?fnVariableName?=?function()?{?throw?new?Error("foo");?};
          ??fnVariableName();
          }

          在Firefox中追溯幀格式如下:

          throwErrorFromInnerFunctionAssignedToVariable/fnVariableName@http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37


          在其他的瀏覽器,追溯幀格式如下:

          at?fnVariableName?(http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)

          在一個函數(shù)定義在另外一個函數(shù)內(nèi)部的情景下(閉包)Firefox會使用不同于其他瀏覽器廠商的格式來處理函數(shù)名

          displayName 屬性

          除了IE11,函數(shù)名的展現(xiàn)也可以通過給函數(shù)定義一個displayName 屬性,displayName會出現(xiàn)在瀏覽器的devtools debugger中。而Safari displayName還會出現(xiàn)在追溯幀中。

          var?someFunction?=?function()?{};
          someFunction.displayName?=?"?#?A?longer?description?of?the?function.";

          雖然關(guān)于displayName還沒有官方的標(biāo)準(zhǔn),但是該屬性已經(jīng)在主要的瀏覽器中實現(xiàn)了。通過如下網(wǎng)站你可以了解更多關(guān)于displayName的信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName 和 http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/

          IE11不支持displayName屬性

          Safari displayName property bug Safari 會使用displayName作為函數(shù)名在追溯幀中出現(xiàn)

          通過編程來獲取追溯棧

          當(dāng)拋出一個錯誤但又沒有追溯棧的時候(通過下面的內(nèi)容了解更多),我們可以通過一些編程的手段來捕獲追溯棧。

          在Chrome中,可以簡單的調(diào)用Error.captureStackTrace API來獲取到追溯棧,關(guān)于該API的使用可以通過如下鏈接了解:https://github.com/v8/v8/wiki/Stack%20Trace%20API

          舉個例子:

          function?ignoreThisFunctionInStackTrace()?{
          ??var?err?=?new?Error();
          ??Error.captureStackTrace(err,?ignoreThisFunctionInStackTrace);
          ??return?err.stack;
          }

          在其它瀏覽器中,追溯棧也可以通過生成一個錯誤,然后通過stack屬性來獲取追溯棧。

          var?err?=?new?Error('');
          return?err.stack;

          但是在IE10中,只有當(dāng)錯誤真正拋出后才能夠獲取到追溯棧。

          try?{
          ??throw?new?Error('');
          }?catch?(e)?{
          ??return?e.stack;
          }

          如果上面的方法都起作用時,我們可以通過arguments.callee.caller 對象來粗糙的獲取一個沒有行數(shù)和列數(shù)的追溯棧,但是這種方法在ES5嚴(yán)格模式下不起作用,因此這種方法也不是一種推薦的做法。

          Async stack traces

          異步追溯棧

          在JavaScript代碼中異步代碼是非常常見的。比如setTimeout的使用,或者Promise對象的使用,這些異步調(diào)用入口往往會給追溯棧帶來問題,因為異步代碼會生成一個新的執(zhí)行上下文,而追溯棧又會重新形成追溯幀。

          Chrome DevTools 已經(jīng)支持了異步追溯棧,換句話說,追溯棧在追溯一個錯誤的時候也會顯示引入異步調(diào)用的那一調(diào)用幀。在使用setTimeout的情況下,在Chrome中會捕獲誰調(diào)用了產(chǎn)生錯誤的setTimeout 函數(shù)。關(guān)于上面內(nèi)容,可以從如下網(wǎng)站獲取信息:http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/

          一個異步追溯棧會采用如下形式:

          ??throwError????@???throw-error.js:2
          ??setTimeout?(async)????????
          ??throwErrorAsync???@???throw-error.js:10
          ??(anonymous?function)??@???throw-error-basic.html:14

          目前,異步追溯棧只有Chrome DevTools支持,而且只有在DevTools代開的情況下才會捕獲,在代碼中通過Error對象不會獲取到異步追溯棧。

          雖然可以模擬異步調(diào)用棧,但是這往往會代指應(yīng)用性能的消耗,因為這種方法也顯得并不可取。

          Only Chrome supports async stack traces 只有Chrome DevTools原生支持異步追溯棧

          命名行內(nèi)JS代碼或者使用eval情況

          在追溯使用eval或者HTML 中寫JS的情況,追溯棧通常會使用HTML的URL 以及代碼執(zhí)行的行數(shù)和列數(shù)。

          例如:

          ??at?throwError?(http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9)
          ??at?http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3

          出于一些性能或代碼優(yōu)化的原因,HTML中往往會有行內(nèi)腳本,而且這種情況下,URL, 行數(shù)、列數(shù)也有可能出錯,為了解決這些問題,Chrome和Firefox 支持//# sourceURL= 聲明,(Safari 和 IE 暫不支持)。通過這種形式聲明的URL會在追溯棧中使用到,而且行數(shù)和列數(shù)也會通過

          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    一区二区三区黄色电影 | 在线操比 | 亚洲AAA在线观看 | 午夜福利免费视频在线观看 | 国产成人亚洲日韩欧美四虎 |