16個(gè)你可能不知道的JavaScript調(diào)試技巧
對(duì)于調(diào)試JavaScript,當(dāng)然是越快越好,我們?cè)谶@里列舉了一些如何更加快速高效調(diào)試JS的重要竅門。
更加快速地調(diào)試JavaScript
熟悉了解工具對(duì)于完成任務(wù)來(lái)說(shuō)有著重要的作用。盡管JavaScrip是t出了名的難調(diào)試,但如果你知道一些竅門,那你可以花更少的時(shí)間去解決這些error和bug。
我們?cè)谶@里列舉了16個(gè)你可能不知道的調(diào)試技巧,但它們可能是你想要在下次調(diào)試JavaScript代碼之前記住的。
如果你想更快地調(diào)試JavaScript的bug,你可以嘗試Raygun崩潰報(bào)告( Raygun Crash Reporting ),它可以提供給你關(guān)于bugs的告警以及堆棧追蹤(stack trace)。
雖然許多技巧也同樣適用于其他的檢查器(inspector),但這里提及的大部分的技巧是基于Chrome和Firefox的。
1. debugger
除了console.log,debugger是我最喜歡的快速直接的調(diào)試工具。如果你在代碼中嵌入debugger;,Chrome執(zhí)行到這里時(shí)會(huì)自動(dòng)停止。你甚至可以把它放在條件語(yǔ)句中,這樣它只有在你需要的時(shí)候才會(huì)運(yùn)行。
- if (thisThing) {
- debugger;
- }
- if (thisThing) {
- debugger;
- }
2. 在表格中顯示對(duì)象
有時(shí)候你想看一組特別復(fù)雜的對(duì)象。你可以用console.log來(lái)打印出整個(gè)列表,也可以使用console.table來(lái)幫助顯示。這樣可以使你更簡(jiǎn)單明了地看到你所處理的對(duì)象。
- var animals = [
- { animal: 'Horse', name: 'Henry', age: 43 },
- { animal: 'Dog', name: 'Fred', age: 13 },
- { animal: 'Cat', name: 'Frodo', age: 18 }
- ];
- console.table(animals);
它將會(huì)輸出:
3. 嘗試所有的尺寸
盡管在你的桌上擁有每一種移動(dòng)設(shè)備特別棒,但這并不現(xiàn)實(shí)。不如重新調(diào)整視覺視口(viewport)的大小?Chrome提供了你所需要的一切。打開瀏覽器的inspector并點(diǎn)擊選中設(shè)備模式(device mode)的按鈕。你就可以觀察到媒體查詢(media queries)了。
4. 如何快速找到你需要的DOM元素
在元素面板中標(biāo)記一個(gè)DOM元素,并在控制臺(tái)中使用它。Chrome的Inspector會(huì)保存歷史記錄中最近的5個(gè)元素,最后被標(biāo)記的元素會(huì)被顯示成 1,以此類推。如果你按順序標(biāo)記“item4“,”item-3“,”item-2“,”item-1“,”item-0“,那么你可以像下圖這樣在控制臺(tái)中訪問(wèn)DOM節(jié)點(diǎn)。
5. 用console.time()和console.timeEnd()來(lái)衡量loops快慢
知道一段代碼到底需要多長(zhǎng)時(shí)間來(lái)執(zhí)行是很有幫助的,尤其是當(dāng)你在調(diào)試執(zhí)行速度慢的代碼的時(shí)候。你甚至可以通過(guò)給函數(shù)賦上不同的標(biāo)簽值來(lái)設(shè)置多個(gè)timer。讓我們看看它是怎么工作的:
- console.time('Timer1');
- var items = [];
- for (var i = 0; i < 100000; i++) {
- items.push({index: i});
- }
- console.timeEnd('Timer1');
它將會(huì)輸出:
6. 得到函數(shù)的堆棧追蹤
你可能知道JavaScript框架可以快速產(chǎn)生很多代碼。
你會(huì)有許多的視圖,并且觸發(fā)很多事件,所以你終究會(huì)遇到一個(gè)情況,會(huì)想知道是什么觸發(fā)了一個(gè)特定的函數(shù)調(diào)用。因?yàn)镴avaScript不是一個(gè)非常具有結(jié)構(gòu)性的語(yǔ)言,所以有時(shí)候你很難知道發(fā)生了什么事,什么時(shí)候發(fā)生了這件事。這就是使用console.trace(或者直接在控制臺(tái)中使用trace)的時(shí)候了。試想一下你想查看在第33行car實(shí)例中關(guān)于funcZ的函數(shù)調(diào)用的整個(gè)堆棧追蹤:
- var car;
- var func1 = function() {
- func2();
- }
- var func2 = function() {
- func4();
- }
- var func3 = function() {
- }
- var func4 = function() {
- car = new Car();
- car.funcX();
- }
- var Car = function() {
- this.brand = ‘volvo’;
- this.color = ‘red’;
- this.funcX = function() {
- this.funcY();
- }
- this.funcY = function() {
- this.funcZ();
- }
- this.funcZ = function() {
- console.trace(‘trace car’)
- }
- }
- func1();
- var car;
- var func1 = function() {
- func2();
- }
- var func2 = function() {
- func4();
- }
- var func3 = function() {
- }
- var func4 = function() {
- car = new Car();
- car.funcX();
- }
- var Car = function() {
- this.brand = ‘volvo’;
- this.color = ‘red’;
- this.funcX = function() {
- this.funcY();
- }
- this.funcY = function() {
- this.funcZ();
- }
- this.funcZ = function() {
- console.trace(‘trace car’)
- }
- }
- func1();
第33行會(huì)輸出:
現(xiàn)在我們可以發(fā)現(xiàn)func1調(diào)用了func2,func2調(diào)用了func4,而func4創(chuàng)建了一個(gè)Car的實(shí)例并調(diào)用了car.FuncX,以此類推。盡管你認(rèn)為自己已經(jīng)對(duì)腳本特別熟悉,你仍然會(huì)發(fā)現(xiàn)這個(gè)方法特別得方便。如果你想提高你的代碼質(zhì)量,你可以獲取所有的追蹤和相關(guān)的函數(shù)。它們中的每個(gè)都是可點(diǎn)擊的,你可以來(lái)回切換,對(duì)你來(lái)說(shuō)就像一個(gè)菜單一樣。
7. 解壓縮代碼使得調(diào)試JavaScript更加簡(jiǎn)單
有時(shí)候你可能會(huì)在線上的產(chǎn)品中遇到這樣的問(wèn)題,服務(wù)器并不能提供源代碼映射(souce maps)的功能。不用怕,Chrome可以將你的JavaScript文件解壓縮成更加易讀的形式。這些代碼不會(huì)和真的代碼一樣有幫助,但是至少你能明白發(fā)生了什么。你可以點(diǎn)擊在inspector中源代碼視圖下方的 {}
按鈕來(lái)格式化代碼。
8. 快速找到需要被調(diào)試的函數(shù)
如果你想要在一個(gè)函數(shù)中設(shè)置一個(gè)斷點(diǎn),有兩種方法:
- 在inspector中找到那行,添加一個(gè)斷點(diǎn)
- 在腳本代碼中添加debugger
上述的兩種方法,你都需要自己手動(dòng)在文件中找出你需要調(diào)試的特定的那一行代碼。一個(gè)比較不為人知的辦法是,在控制臺(tái)中使用debug(函數(shù)名),這樣腳本會(huì)在執(zhí)行到這個(gè)函數(shù)時(shí)自動(dòng)停下。
這個(gè)方法特別快,但缺點(diǎn)是它沒法使用在私有函數(shù)和匿名函數(shù)上。除此之外,這大概是最快找到需要調(diào)試函數(shù)的方法了。(注意:有一個(gè)函數(shù)叫做console.debug,盡管名字相似,但和這個(gè)并不是一回事)
- var car;
- var func1 = function() {
- func2();
- }
- var func2 = function() {
- func4();
- }
- var func3 = function() {
- }
- var func4 = function() {
- car = new Car();
- car.funcX();
- }
- var Car = function() {
- this.brand = ‘volvo’;
- this.color = ‘red’;
- this.funcX = function() {
- this.funcY();
- }
- this.funcY = function() {
- this.funcZ();
- }
- this.funcZ = function() {
- console.trace(‘trace car’)
- }
- }
- func1();
- var car;
- var func1 = function() {
- func2();
- }
- var func2 = function() {
- func4();
- }
- var func3 = function() {
- }
- var func4 = function() {
- car = new Car();
- car.funcX();
- }
- var Car = function() {
- this.brand = ‘volvo’;
- this.color = ‘red’;
- this.funcX = function() {
- this.funcY();
- }
- this.funcY = function() {
- this.funcZ();
- }
- this.funcZ = function() {
- console.trace(‘trace car’)
- }
- }
- func1();
在控制臺(tái)中輸入debug(car.funcY),腳本會(huì)在有函數(shù)調(diào)用car.funcY時(shí)停下并進(jìn)入調(diào)試模式。
9. 排除與bug無(wú)關(guān)的腳本
如今我們?cè)趙eb應(yīng)用中會(huì)用到許多的庫(kù)和框架,它們中的大部分是經(jīng)過(guò)完整的測(cè)試并且相對(duì)而言可以被假設(shè)成是沒有bug的。但是debugger還是會(huì)步入所有這些與調(diào)試任務(wù)沒有關(guān)系的文件。解決的方法是把你不需要調(diào)試的腳本放進(jìn)黑盒里,這里頭也可以包括一些你自己寫的腳本代碼。你可以在這篇調(diào)試黑盒的 文章 中閱讀到更多的細(xì)節(jié)。
https://raygun.com/blog/javascript-debugging-with-black-box/
10. 在復(fù)雜的調(diào)試中找到重要的事情
在更多復(fù)雜的調(diào)試情境中,我們有時(shí)會(huì)想要輸出許多行。在這里,我們可以利用各種console函數(shù)來(lái)使輸出使輸出的內(nèi)容保持一個(gè)更易讀的結(jié)構(gòu)。比如,console.log,console.debug,console.warn,console.info,console.error等等。你可以在inspector中篩選它們。有時(shí)候這可能不是你在調(diào)試時(shí)想要達(dá)到的效果,但如果需要,你可以使你輸出的信息更加多樣性并有自己的風(fēng)格。當(dāng)你想調(diào)試JavaScript時(shí),你可以使用CSS來(lái)創(chuàng)建你自己的格式化控制臺(tái)信息:
- console.todo = function(msg) {
- console.log(‘ % c % s % s % s‘, ‘color: yellow; background - color: black;’, ‘–‘, msg, ‘–‘);
- }
- console.important = function(msg) {
- console.log(‘ % c % s % s % s’, ‘color: brown; font - weight: bold; text - decoration: underline;’, ‘–‘, msg, ‘–‘);
- }
- console.todo(“This is something that’ s need to be fixed”);
- console.important(‘This is an important message’);
它將會(huì)輸出:
在console.log()中,你可以使用%s來(lái)代表string,%i來(lái)代表integers,%c來(lái)代表自定義風(fēng)格。你可能可以找到其他更好的方法來(lái)使用這些風(fēng)格。如果你使用單頁(yè)應(yīng)用框架(single page framework),你或許想要message對(duì)應(yīng)一種風(fēng)格,model對(duì)應(yīng)一種風(fēng)格,collections對(duì)應(yīng)一種風(fēng)格,而controller對(duì)應(yīng)另一種風(fēng)格,等等。
11. 查看特定的函數(shù)調(diào)用和參數(shù)
在Chrome的控制臺(tái)中,你可以查看一些特定的函數(shù)。每次這些函數(shù)被調(diào)用時(shí),傳入函數(shù)的參數(shù)就會(huì)被輸出。
- var func1 = function(x, y, z) {
- //....
- };
它會(huì)輸出:
這是一個(gè)查看傳入函數(shù)參數(shù)的好辦法。最理想的情況下,控制臺(tái)可以辨別出函數(shù)需要多少個(gè)參數(shù),但實(shí)際上并不能。在上述的例子中,func1需要三個(gè)參數(shù),但只傳入了兩個(gè)。如果在代碼中沒有做好處理,這樣會(huì)導(dǎo)致潛在的bug。
12. 快速在控制臺(tái)中訪問(wèn)元素
一個(gè)在控制臺(tái)中更快地完成選擇器查詢(querySelector)的方法是使用. ('css-selector')會(huì)返回第一個(gè)匹配,.$$('css-selector')會(huì)返回所有的匹配。如果你使用一個(gè)元素多于一次,那么可以把它存為一個(gè)變量。
13. Postman是非常好的工具
許多開發(fā)者使用Postman去配合Ajax請(qǐng)求的使用。Postman特別得棒,但令人煩惱的是,每打開一個(gè)新的瀏覽器窗口,就需要寫一個(gè)新的請(qǐng)求對(duì)象,然后再測(cè)試它。
有時(shí)候用瀏覽器更加簡(jiǎn)單。當(dāng)你這么做時(shí),如果是一個(gè)密碼安全頁(yè)面(password-secure page),你不再需要為認(rèn)證cookie擔(dān)心。
在Firefox編輯和重新發(fā)送請(qǐng)求,你只需要打開Insepctor,到Network標(biāo)簽頁(yè)下,右鍵點(diǎn)擊你想要修改的請(qǐng)求然后編輯,再重新發(fā)送。
現(xiàn)在你可以更改任何你想要的東西,更改header,編輯參數(shù),然后點(diǎn)擊重新發(fā)送。
下面我展示了一個(gè)有著兩種不同屬性的請(qǐng)求:
14. 在節(jié)點(diǎn)上設(shè)置斷點(diǎn)
DOM可以是一件特別有趣的事情。有時(shí)候有些東西改變了,你卻不知道為什么。然而,當(dāng)你需要調(diào)試JavaScript時(shí),你可以通過(guò)Chrome使一個(gè)DOM元素停止改變。你甚至可以看見它的屬性。在Chrome的Inspector中,右鍵點(diǎn)擊那個(gè)元素并選擇一個(gè)設(shè)置斷點(diǎn)的條件:
15. 使用頁(yè)面提速服務(wù)(page speed service)
有許多服務(wù)和工具可以幫助你審查頁(yè)面的JavaScript,找出那些運(yùn)行緩慢和有問(wèn)題的地方。其中的一個(gè)工具是 Raygun Real User Monitoring 。除了幫助找到JavaScript問(wèn)題(如外部腳本加載緩慢,不必要的CSS,過(guò)大的圖片),這個(gè)工具還在其他方面也十分有幫助。它還可以幫你找到一些無(wú)意間導(dǎo)致加載時(shí)間過(guò)長(zhǎng),或者腳本無(wú)法正常執(zhí)行的JavaScript問(wèn)題。
你還可以用它來(lái)測(cè)量JavaScript代碼的性能提高,并跟蹤變化。
16. 多使用斷點(diǎn)
最后,合理正確地使用斷點(diǎn)可以幫助你成功調(diào)試。嘗試在不同的場(chǎng)景下用不同的方式使用斷點(diǎn)。
你可以點(diǎn)擊元素來(lái)設(shè)置斷點(diǎn),從而那個(gè)元素被修改時(shí)執(zhí)行會(huì)停止。你也可以在開發(fā)者工具的Debugger或者Sources標(biāo)簽頁(yè)(取決于你的瀏覽器)中,為任何源代碼設(shè)置XHR斷點(diǎn)來(lái)停止Ajax請(qǐng)求。在同樣的地方,當(dāng)異常拋出時(shí)代碼的執(zhí)行也會(huì)被暫停。你可以在瀏覽器中使用各種斷點(diǎn)來(lái)最大化你找到bug的幾率,而不是把時(shí)間花在尋找外部的工具上。
總結(jié)
綜上所述,有許多方法可以幫助調(diào)試JavaScript代碼。我們?cè)谶@兒已經(jīng)列舉了其中的16個(gè)。如果你想開始找一些依靠瀏覽器之外的工具幫助優(yōu)化瀏覽器,這兒還有一篇特別有幫助的 文章 列舉了一些JavaScript的調(diào)試工具。無(wú)論怎樣,你應(yīng)該能夠利用這些調(diào)試技巧開始調(diào)試你的JavaScript代碼了,使你的代碼沒有bug,并為部署和發(fā)布做好準(zhǔn)備!