亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

JS高級調試技巧:捕獲和分析 JavaScript Error詳解

2019-11-20 20:56:40
字體:
來源:轉載
供稿:網友

反正只要 JavaScript 出錯后刷新不復現,那用戶就可以通過刷新解決問題,瀏覽器不會崩潰,當沒有發生過好了。這種假設在 Single Page App 流行之前還是成立的?,F在的 Single Page App 運行一段時間后狀態復雜無比,用戶可能進行了若干輸入操作才來到這里的,說刷新就刷新???之前的操作豈不要完全重做?所以我們還是有必要捕獲和分析這些異常信息的,然后我們就可以修改代碼避免影響用戶體驗。

捕獲異常的方式

我們自己寫的 throw new Error() 想要捕獲當然可以捕獲,因為我們很清楚 throw 寫在哪里了。但是調用瀏覽器 API 時發生的異常就不一定那么容易捕獲了,有些 API 在標準里就寫著會拋出異常,有些 API 只有個別瀏覽器因為實現差異或者有缺陷而拋出異常。對于前者我們還能通過 try-catch 捕獲,對于后者我們必須監聽全局的異常然后捕獲。

try-catch

如果有些瀏覽器 API 是已知會拋出異常的,那我們就需要把調用放到 try-catch 里面,避免因為出錯而導致整個程序進入非法狀態。例如說 window.localStorage 就是這樣的一個 API,在寫入數據超過容量限制后就會拋出異常,在 Safari 的隱私瀏覽模式下也會如此。

try {
localStorage.setItem('date', Date.now());
} catch (error) {
reportError(error);
}

另一個常見的 try-catch 適用場景是回調。因為回調函數的代碼是我們不可控的,代碼質量如何,會不會調用其它會拋出異常的 API,我們一概不知道。為了不要因為回調出錯而導致調用回調后的其它代碼無法執行,所以把調用回到放到 try-catch 里面是必須的。

listeners.forEach(function(listener) {
try {
listener();
} catch (error) {
reportError(error);
}
});

window.onerror

對于 try-catch 覆蓋不到的地方,如果出現異常就只能通過 window.onerror 來捕獲了。

window.onerror =
function(errorMessage, scriptURI, lineNumber) {
reportError({
message: errorMessage,
script: scriptURI,
line: lineNumber
});
}

注意不要耍小聰明使用 window.addEventListenerwindow.attachEvent 的形式去監聽 window.onerror。很多瀏覽器只實現了 window.onerror,或者是只有 window.onerror 的實現是標準的??紤]到標準草案定義的也是 window.onerror,我們使用 window.onerror 就好了。

屬性丟失

假設我們有一個 reportError 函數用來收集捕獲到的異常,然后批量發送到服務器端存儲以便查詢分析,那么我們會想要收集哪些信息呢?比較有用的信息包括:錯誤類型(name)、錯誤消息(message)、腳本文件地址(script)、行號(line)、列號(column)、堆棧跟蹤(stack)。如果一個異常是通過 try-catch 捕獲到的,這些信息都在 Error 對象上(主流瀏覽器都支持),所以 reportError 也能收集到這些信息。但如果是通過 window.onerror 捕獲到的,我們都知道這個事件函數只有 3 個參數,所以這 3 個參數意外的信息就丟失了。

序列化消息

如果 Error 對象是我們自己創建的話,那么 error.message 就是由我們控制的?;旧衔覀儼咽裁捶胚M error.message 里面,window.onerror 的第一個參數(message)就會是什么。(瀏覽器其實會略作修改,例如加上 'Uncaught Error: ' 前綴。)因此我們可以把我們關注的屬性序列化(例如 JSON.Stringify)后存放到 error.message 里面,然后在 window.onerror 讀取出來反序列化就可以了。當然,這僅限于我們自己創建的 Error 對象。

第五個參數

瀏覽器廠商也知道大家在使用 window.onerror 時受到的限制,所以開始往 window.onerror 上面添加新的參數。考慮到只有行號沒有列號好像不是很對稱的樣子,IE 首先把列號加上了,放在第四個參數。然而大家更關心的是能否拿到完整的堆棧,于是 Firefox 說不如把堆棧放在第五個參數吧。但 Chrome 說那還不如把整個 Error 對象放在第五個參數,大家想讀取什么屬性都可以了,包括自定義屬性。結果由于 Chrome 動作比較快,在 Chrome 30 實現了新的 window.onerror 簽名,導致標準草案也就跟著這樣寫了。

window.onerror = function(
errorMessage,
scriptURI,
lineNumber,
columnNumber,
error
) {
if (error) {
reportError(error);
} else {
reportError({
message: errorMessage,
script: scriptURI,
line: lineNumber,
column: columnNumber
});
}
}
屬性正規化

我們之前討論到的 Error 對象屬性,其名稱都是基于 Chrome 命名方式的,然而不同瀏覽器對 Error 對象屬性的命名方式各不相同,例如腳本文件地址在 Chrome 叫做 script 但在 Firefox 叫做 filename。因此,我們還需要一個專門的函數來對 Error 對象進行正規化處理,也就是把不同的屬性名稱都映射到統一的屬性名稱上。具體做法可以參考這篇文章。盡管瀏覽器實現會更新,但人手維護一份這樣的映射表并不會太難。

類似的是堆棧跟蹤(stack)的格式。這個屬性以純文本的形式保存一份異常在發生時的堆棧信息,由于各個瀏覽器使用的文本格式不一樣,所以也需要人手維護一份正則表達,用于從純文本中提取每一幀的函數名(identifier)、文件(script)、行號(line)和列號(column)。

安全限制

如果你也遇到過消息為 'Script error.' 的錯誤,你會明白我在說什么的,這其實是瀏覽器針對不同源(origin)腳本文件的限制。這個安全限制的理由是這樣的:假設一家網銀在用戶登錄后返回的 HTML 跟匿名用戶看到的 HTML 不一樣,一個第三方網站就能把這家網銀的 URI 放到 script.src 屬性里面。HTML 當然不可能被當做 JS 解析啦,所以瀏覽器會拋出異常,而這個第三方網站就能通過解析異常的位置來判斷用戶是否有登錄。為此瀏覽器對于不同源腳本文件拋出的異常一律進行過濾,過濾得只剩下 'Script error.' 這樣一條不變的消息,其它屬性統統消失。

對于有一定規模的網站來說,腳本文件放在 CDN 上,不同源是很正常的?,F在就算是自己做個小網站,常見框架如 jQuery 和 Backbone 都能直接引用公共 CDN 上的版本,加速用戶下載。所以這個安全限制確實造成了一些麻煩,導致我們從 Chrome 和 Firefox 收集到的異常信息都是無用的 'Script error.'。

CORS

想要繞過這個限制,只要保證腳本文件和頁面本身同源即可。但把腳本文件放在不經 CDN 加速的服務器上,豈不降低用戶下載速度?一個解決方案是,腳本文件繼續放在 CDN 上,利用 XMLHttpRequest 通過 CORS 把內容下載回來,再創建 <script> 標簽注入到頁面當中。在頁面當中內嵌的代碼當然是同源的啦。

這說起來很簡單,但實現起來卻有很多細節問題。用一個簡單的例子來說:

<script src="http://cdn.com/step1.js"></script>
<script>
(function step2() {})();
</script>
<script src="http://cdn.com/step3.js"></script>

我們都知道這個 step1、step2、step3 如果存在依賴關系的話,則必須嚴格按照這個順序執行,否則就可能出錯。瀏覽器可以并行請求 step1 和 step3 的文件,但在執行時順序是保證的。如果我們自己通過 XMLHttpRequest 獲取 step1 和 step3 的文件內容,我們就需要自行保證其順序正確性。此外不要忘記了 step2,在 step1 以非阻塞形式下載的時候 step2 就可以被執行了,所以我們還必須人為干預 step2 讓它等待 step1 完成后再執行。

如果我們已經有一整套工具來生成網站上不同頁面的 <script> 標簽的話,我們就需要調整一下這套工具讓它對 <script> 標簽做出改動:

<script>
scheduleRemoteScript('http://cdn.com/step1.js');
</script>
<script>
scheduleInlineScript(function code() {
(function step2() {})();
});
</script>
<script>
scheduleRemoteScript('http://cdn.com/step3.js');
</script>

我們需要實現 scheduleRemoteScriptscheduleInlineScript 這兩個函數,并且保證它們在第一個引用外部腳本文件的 <script> 標簽之前就被定義好,然后余下的 <script> 標簽都會被改寫成上面這種形式。注意原本立即執行的 step2 函數被放到了一個更大的 code 函數里面了。code 函數并不會被執行,它只是一個容器而已,這樣使得原本 step2 的代碼不需要轉義就能保留下來,但又不會被立即執行。

接下來我們還需要實現一套完整的機制,保證這些由 scheduleRemoteScript 根據地址下載回來的文件內容和由 scheduleInlineScript 直接獲取到的代碼能夠按照正確的順序一個接一個地執行。詳細的代碼我就不在這里給出了,大家有興趣可以自己去實現。

行號反查

通過 CORS 獲取內容再把代碼注入頁面能夠突破安全限制,但會引入一個新的問題,那就是行號沖突。原本通過 error.script 可以定位到唯一的腳本文件,再通過 error.line 可以定位到唯一的行號?,F在由于都是頁面內嵌的代碼,多個 <script> 標簽并不能通過 error.script 來區分,然而每一個 <script> 標簽內部的行號都是從 1 算起的,結果就導致我們無法利用異常信息定位錯誤所在的源代碼位置。

為了避免行號沖突,我們可以浪費一些行號,使得每一個 <script> 標簽中有實際代碼所使用的行號區間互相不重疊。舉個例子來說,假設每個 <script> 標簽中的實際代碼都不超過 1000 行,那么我可以讓第一個 <script> 標簽中的代碼占用第 1亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

在线亚洲欧美视频| 成人午夜高潮视频| 成人福利视频网| 久久久久久久久中文字幕| 91精品国产91久久久久福利| 亚洲一区二区免费在线| 美日韩精品免费观看视频| 国产精品福利在线| 2019最新中文字幕| 久久久久久久久久av| 日韩精品免费在线观看| 中文字幕欧美日韩va免费视频| 91精品久久久久久久久久久| 欧美在线观看网站| 亚洲精品国产美女| 国产精品第3页| 欧美日韩国产精品| 91亚洲国产精品| 大胆人体色综合| 91国产精品视频在线| 国产成人综合久久| 中文字幕一区电影| 亚洲欧美日韩网| 亚洲精品视频网上网址在线观看| 欧美激情图片区| 欧美成人午夜激情在线| 亚洲男人天堂2019| 国产精品免费看久久久香蕉| 亚洲精品一区在线观看香蕉| 亚洲免费电影在线观看| 精品综合久久久久久97| 国产精品久久久久一区二区| 欧美视频不卡中文| 久久免费在线观看| 成人久久18免费网站图片| 777精品视频| 日韩有码在线视频| 96sao精品视频在线观看| 欧美成年人在线观看| 亚洲欧洲国产精品| 久久久av亚洲男天堂| 色综合久久久久久中文网| 日韩av免费在线播放| 国产精品久久久久99| www.欧美三级电影.com| 亚洲国产精品免费| 欧美大片欧美激情性色a∨久久| 欧美裸体xxxxx| 午夜精品蜜臀一区二区三区免费| 在线视频欧美性高潮| 亚洲一区二区在线| 国产精品亚洲美女av网站| 国内精品久久影院| 久久精品一区中文字幕| 国产免费一区二区三区香蕉精| 国产成人一区二区| 欧美亚洲在线观看| 日韩在线观看免费全集电视剧网站| 疯狂做受xxxx欧美肥白少妇| 国产成+人+综合+亚洲欧美丁香花| 97在线视频一区| 国产在线999| 国产精品免费一区| 午夜精品久久久久久久99热浪潮| 亚洲视频电影图片偷拍一区| 在线视频日韩精品| 久久男人av资源网站| 日本精品中文字幕| 亚洲国产三级网| 亚洲精品720p| 久久精品中文字幕电影| 欧美精品福利视频| 欧美国产中文字幕| 亚洲欧美激情另类校园| 国产亚洲免费的视频看| 久久精品国产电影| 国产成人精品一区| 97在线视频国产| 青青青国产精品一区二区| 亚洲成av人影院在线观看| 原创国产精品91| 福利视频第一区| 精品色蜜蜜精品视频在线观看| 亚洲国产精彩中文乱码av在线播放| 久久精品一本久久99精品| 日韩精品免费观看| 成人在线视频网| 红桃av永久久久| 久久综合亚洲社区| 成人高清视频观看www| 欧美日韩加勒比精品一区| 国产欧美日韩最新| y97精品国产97久久久久久| 国产国语videosex另类| yellow中文字幕久久| 韩国国内大量揄拍精品视频| 欧美黑人xxxⅹ高潮交| 精品国产一区av| 国语对白做受69| 国产欧美在线播放| 国产激情999| 欧美性xxxx极品高清hd直播| 国产精品久久久久久久久久东京| 日韩中文字幕国产| 欧美亚洲视频一区二区| 日韩精品在线免费观看视频| 精品av在线播放| 欧美极品少妇全裸体| 国产97色在线|日韩| 亚洲欧美国产精品专区久久| 欧美中文在线观看国产| 国产欧美 在线欧美| 久久久久久97| 日韩美女写真福利在线观看| 久99久在线视频| 日本成人免费在线| 日韩国产精品亚洲а∨天堂免| 日韩免费av片在线观看| 亚洲有声小说3d| 亚洲精品少妇网址| 性金发美女69hd大尺寸| 韩国日本不卡在线| 亚洲欧美日本伦理| 视频一区视频二区国产精品| 色哟哟网站入口亚洲精品| 97国产精品人人爽人人做| 97在线视频精品| 欧美成人精品不卡视频在线观看| 日韩资源在线观看| 国产欧美日韩亚洲精品| 亚洲电影中文字幕| 亚洲国产精品嫩草影院久久| 成人免费直播live| 成人在线观看视频网站| 欧美视频中文字幕在线| 欧美夫妻性视频| 国产日韩欧美在线看| 欧美激情在线播放| 欧美黑人国产人伦爽爽爽| 国产精品一区二区三区在线播放| 2021久久精品国产99国产精品| 国产女人18毛片水18精品| 国产99视频在线观看| xxx成人少妇69| 亚洲最新av在线网站| 欧美日韩精品在线视频| 色爱精品视频一区| 奇米成人av国产一区二区三区| 亚洲第一区第二区| 日韩欧美极品在线观看| 亚洲天堂影视av| 欧美激情一区二区三级高清视频| 日本精品久久久久久久| 国产精品9999| 国产成一区二区| www日韩欧美| 国产精品入口免费视| 在线观看视频亚洲| www亚洲精品| 久久青草精品视频免费观看| 91亚洲午夜在线| 久久久国产一区二区三区| 95av在线视频| 久久久av亚洲男天堂|