js断点和调试学习总结3
生活随笔
收集整理的這篇文章主要介紹了
js断点和调试学习总结3
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
使用Chrome調試JavaScript的斷點設置和調試技巧
你是怎么調試 JavaScript 程序的?最原始的方法是用 alert() 在頁面上打印內容,稍微改進一點的方
法是用 console.log() 在 JavaScript 控制臺上輸出內容。嗯~,用這兩種土辦法確實解決了很多小型?
JavaScript 腳本的調試問題。不過放著 Chrome 中功能越發強大的開發者工具不用實在太可惜了。本文
主要介紹其中的 JavaScript斷點設置和調試功能,也就是其中的 Sources Panel(以前叫 Scripts)。
如果你精通 Eclipse 中的各種 Java 調試技巧,那么這里的概念都是類似。寫作本文時使用的 Chrome?
版本為 25.0.1364.172。
基本環境
SourcesPanel 的左邊是內容源,包括頁面中的各種資源。其中,又分 Sources 和 Content scripts。Sources 就是頁面本身包含的各種資源,它是按照頁面中出現的域來組織的,這是我們要關注的。異步
加載的 js 文件,在加載后也會出現在這里的。Content scripts 是 Chrome 的一種擴展程序,它是按
照擴展的ID來組織的,這類擴展實際也是嵌入在頁面中的資源,它們也可以讀寫DOM。編寫、調試這類擴
展的開發者才要關心它們,如果你的瀏覽器沒安裝任何擴展,那么Content scripts 就看不到任何東西
。
Sources Panel 的中間主區域用于展示左邊資源文件的內容。
Sources Panel 的右邊是調試功能區,最上面的一排按鈕分別是暫停/繼續、單步執行、單步跳入、單步
跳出、禁用/啟用所有斷點。下面是各種具體的功能區。稍后介紹。
注意,左右兩邊的區域默認可能收縮在兩側沒有顯示出來,點擊兩側的伸縮按鈕展示出來。左邊區域展
示出來時默認是自動收縮狀態,點擊旁邊的 pin 按鈕就不會縮回去了。
最下面還有一些功能按鈕很有用。
在源代碼上設置斷點
通過左邊的內容源,打開對應的 JavaScript 文件,鼠標點擊文件的行號就可以設置和刪除斷點。添加的每個斷點都會出現在右側調試區的 Breakpoints 列表中,點擊列表中斷點就會定位到內容區的斷點上
。如果你有多個文件、多個斷點的話,利用Breakpoints 列表中的斷點快速定位非常方便。
對于每個已添加的斷點都有兩種狀態:激活和禁用。剛添加的斷點都是激活狀態,禁用狀態就是保留斷
點但臨時取消該斷點功能。在Breakpoints 列表中每個斷點前面都有一個復選框,取消選中就將禁用該
斷點。斷點位置的右鍵菜單中也可以禁用斷點。也可以在右側功能區上面按鈕臨時禁用所有已添加的斷
點,再點一下恢復原狀態。
條件斷點:在斷點位置的右鍵菜單中選擇“Edit Breakpoint...”可以設置觸發斷點的條件,就是寫一
個表達式,表達式為 true 時才觸發斷點。查看斷點的環境調用棧(Call Stack):在斷點停下來時,右側調試區的 Call Stack 會顯示當前斷點所處的方法調用棧,比如有一個函數 g() 其中又調用了函數?
f() ,而我在 f() 中設置了一個斷點,那么我在 console 中執行函數 g() 的時候會觸發斷點,其調用
棧顯示如下:
最上面是 f(),然后是 g()。調用棧中的每一層叫做一個 frame,點擊每個 frame 可以跳到該 frame?
的調用點上。
此外,還可以在 frame 上用右鍵菜單重新開始執行當前 frame,也就是從該 frame 的開始處執行。比
如在函數 f() 的 frame 上 Restart Frame, 斷點就會跳到 f() 的開頭重新執行,context 中的變量
值也會還原。這樣結合變量修改和編輯代碼等功能,就可以在當前 frame 中反復進行調試,而不用刷新
頁面重新觸發斷點了。查看變量
Call Stack 列表的下方是 Scope Variables 列表,在這里可以查看此時局部變量和全局變量的值。執
行選擇的代碼
在斷點調試時,可以用鼠標選擇想要查看的變量或表達式,然后右鍵菜單執行“Evaluate in Console”
,就可以看到該表達式的當前的值了。中斷下次要執行的 JavaScript 語句右側調試區的上面的“中斷/
繼續”按鈕還有一個功能,在沒有觸發斷點時,點一下這個按鈕就會進入“準備”中斷的狀態,頁面下
一次執行 JavaScript 語句時會自動中斷,比如觸發了一個點擊動作時會執行的代碼。臨時修改?
JavaScript 代碼通常我們在調試代碼時習慣:修改代碼→刷新頁面→重新檢查,這么一個循環。但其實?
Chrome 中可以臨時修改 JS 文件中的內容,保存(Ctrl+S)就可以立即生效,結合 Console 等功能就
可以立即重新調試了。但注意這個修改是臨時的,刷新頁面修改就沒了。
異常時斷點
在界面下方能看到按鈕,它是設置程序運行時遇到異常時是否中斷的開關。點擊該按鈕會在3種狀態間切
換:
默認遇到異常不中斷
遇到所有異常都會中斷,包括已捕獲的情況
僅在出現未捕獲的異常時才中斷
主要解釋一下狀態2和狀態3的區別
try{ throw 'a exception'; }catch(e){ console.log(e); }
上面 try 里面的代碼會遇到異常,但是后面的 catch 代碼能夠捕獲該異常。如果是所有異常都中斷,
那么代碼執行到會產生異常的 throw 語句時就會自動中斷;而如果是僅遇到未捕獲異常才中斷,那么這
里就不會中斷。一般我們會更關心遇到未捕獲異常的情況。
在 DOM 元素上設置斷點
有時候我們需要監聽某個 DOM 被修改情況,而不關心是哪行代碼做的修改(也可能有多處都會對其做修
改)。那么我們可以直接在 DOM 上設置斷點。
如圖所見,在元素審查的 Elements Panel 中在某個元素上右鍵菜單里可以設置三種不同情況的斷點:
子節點修改自身屬性修改自身節點被刪除選中之后,Sources Panel 中右側的 DOM Breakpoints 列表中
就會出現該 DOM 斷點。一旦執行到要對該 DOM 做相應修改時,代碼就會在那里停下來,如下圖所示。
XHR 斷點
右側調試區有一個 XHR Breakpoints,點擊+ 并輸入 URL 包含的字符串即可監聽該 URL 的 Ajax 請求
,輸入內容就相當于 URL 的過濾器。如果什么都不填,那么就監聽所有 XHR 請求。一旦 XHR 調用觸發
時就會在 request.send() 的地方中斷。
按事件類型觸發斷點
右側調試區的Event Listener 列表,這里列出了各種可能的事件類型。勾選對應的事件類型,當觸發了
該類型的事件的 JavaScript 代碼時就會自動中斷。
所有開發工具中的快捷鍵都可以在界面右下角的設置中查到。斷點調試時一般用的是 F8、F10、F11或?
Shitf+F11,但在 Mac OS 上 F10 等功能鍵可能與系統默認的快捷鍵沖突。沒關系,它們分別可以用?
Cmd+/ 、Cmd+'、Cmd+; 、Shift+Cmd+; 代替。//@ sourceURL 給 eval 出來的代碼命名有時候一些非常
動態的代碼是以字符串的形式通過 eval() 函數在當前 Javascript context 中創建出來,而不是作為
一個獨立的 js 文件加載的。這樣你在左邊的內容區就找不到這個文件,因此很難調試。其實我們只要
在 eval 創建的代碼末尾添加一行 “//@ sourceURL=name“就可以給這段代碼命名(瀏覽器會特殊對待
這種特殊形式的注釋),這樣它就會出現在左側的內容區了,就好像你加載了一個指定名字的 js 文件
一樣,可以設置斷點和調試了。下圖中,我們通過 eval 執行了一段代碼,并利用sourceURL 將它命名
為dynamicScript.js ,執行后左側內容區就出現了這個“文件”,而它的內容就是 eval 的中的內容。
還可以看看這個給動態編譯出來的CoffeeScript 代碼命名的示例:
var coffee = CoffeeScript.compile(code.value)+ "//@ sourceURL=" + (evalName.value ||?
"Coffeeeeeeee!");
eval(coffee);
實際上,//@ sourceURL 不僅僅可以用在 eval 的代碼中,任何 js 文件、甚至是 Javascript Console?
輸入的代碼都可以用,效果一樣!格式化代碼(Pretty Print)按鈕用于把雜亂的代碼重新格式化為漂
亮的代碼,比如一些已被壓縮的 js 文件基本沒法看、更沒法調試。點一下格式化,再點一下就取消格
式化。
========
JavaScript調試技巧之console.log()詳解
對于JavaScript程序的調試,相比于alert(),使用console.log()是一種更好的方式,原因在于:
alert()函數會阻斷JavaScript程序的執行,從而造成副作用;而console.log()僅在控制臺中打印相關
信息,因此不會造成類似的顧慮
一、什么是console.log()?
除了一些很老版本的瀏覽器,現今大多數瀏覽器都自帶調試功能;即使沒有調試功能,也可以通過安裝
插件來進行補充。比如,老版本的Firefox沒有自帶調試工具,在這種情況下可以通過安裝Firebug插件
來添加調試功能。在具備調試功能的瀏覽器上,window對象中會注冊一個名為console的成員變量,指代
調試工具中的控制臺。通過調用該console對象的log()函數,可以在控制臺中打印信息。比如,以下代
碼將在控制臺中打印”Sample log”:
復制代碼 代碼如下:
window.console.log("Sample log");
上述代碼可以忽略window對象而直接簡寫為:
復制代碼 代碼如下:
console.log("Sample log");
console.log()可以接受任何字符串、數字和JavaScript對象。與alert()函數類似,console.log()也可
以接受換行符\n以及制表符\t。console.log()語句所打印的調試信息可以在瀏覽器的調試控制臺中看到
。不同的瀏覽器中console.log()行為可能會有所不同, 本文主要探討Firebug中console.log()的使用?
。
二、兼容沒有調試控制臺的瀏覽器
對于缺少調試控制臺的老版本瀏覽器,window中的console對象并不存在,因此直接使用console.log()
語句可能會在瀏覽器內部造成錯誤(空指針錯誤),并最終導致某些老版本瀏覽器的崩潰。為了解決這一
問題,可以人為定義console對象,并聲明該console對象的log函數為空函數;這樣,當console.log()
語句執行時,這些老版本的瀏覽器將不會做任何事情:
代碼如下:
if(!window.console){window.console = {log : function(){}}; }
不過,在大多數情況下,沒有必要去做這種兼容性工作 — console.log()等調試代碼應當從最終的產品
代碼中刪除掉。
三、使用參數
與alert()函數類似,console.log()也可以接受變量并將其與別的字符串進行拼接:
復制代碼 代碼如下:
//Use variable
var name = "Bob";
console.log("The name is: " + name);
與alert()函數不同的是,console.log()還可以接受變量作為參數傳遞到字符串中,其具體語法與C語言
中的printf語法一致:
復制代碼 代碼如下:
//Use parameter
var people = "Alex";
var years = 42;
console.log("%s is %d years old.", people, years);
上述代碼的執行結果為:”Alex is 42 years old.”
四、使用其它日志級別
除了console.log(),Firebug還支持多種不同的日志級別:debug、info、warn、error。以下代碼將在
控制臺中打印這些不同日志級別的信息:
代碼如下:
//Use different logging level console.log("Log level"); console.debug("Debug level"); console.info("Info level"); console.warn("Warn level"); console.error("Error level");
從Firebug控制臺中可以看到,不同日志級別的打印信息,其顏色和圖標是不一樣的;同時,可以在控制
臺中選擇不同的日志級別來對這些信息進行過濾:?
========
JS調試必備的5個debug技巧
我一直使用printf調試程序,一般來說都是比較順利,但有時候,你會發現需要更好的方法。下面幾個
JavaScript技巧相信你一定會覺得十分有用
1. debugger;
我以前也說過,你可以在JavaScript代碼中加入一句debugger;來手工造成一個斷點效果。
需要帶有條件的斷點嗎?你只需要用if語句包圍它:
復制代碼 代碼如下:
if (somethingHappens) {
? debugger;
}
但要記住在程序發布前刪掉它們。
2. 設置在DOM node發生變化時觸發斷點
有時候你會發現DOM不受你的控制,自己會發生一些奇怪的變化,讓你很難找出問題的根源。
谷歌瀏覽器的開發工具里有一個超級好用的功能,專門可以對付這種情況,叫做“Break on…”,你在
DOM節點上右鍵,就能看到這個菜單項。
斷點的觸發條件可以設置成這個節點被刪除、節點的屬性有任何變化,或它的某個子節點有變化發生。
3. Ajax 斷點
XHR斷點,或Ajax斷點,就像它們的名字一樣,可以讓我們設置一個斷點,在特點的Ajax調用發生時觸發
它們。
當你在調試Web應用的網絡傳輸時,這一招非常的有效。
4. 移動設備模擬環境
谷歌瀏覽器里有一些非常有趣的模擬移動設備的工具,幫助我們調試程序在移動設備里的運行情況。
找到它的方法是:按F12,調出開發者工具,然后按ESC鍵(當前tab不能是Console),你就會看到第二層
調試窗口出現,里面的Emulation標簽頁里有各種模擬設備可選。
當然,這不會就變成了真正的iPhone,只是模擬了iPhone的尺寸,觸摸事件和瀏覽器User Agent值。
5. 使用Audits改進你的網站
YSlow是一個非常棒的工具。谷歌瀏覽器的開發者工具里也有一個非常類似的工具,叫Audits。
它可快速的審計你的網站,給你提出非常實際有效的優化你的網站的建議和方法。
還有其它的嗎?
沒有這些工具,我不知道將如何開發。我還會寫更多的關于這方面的技巧——一旦我有所發現,請關注
我的最新文章。
========
必備的JS調試技巧匯總
試想一下:出現了某個bug,有人用幾分鐘就搞定了,有人用了半天或者一天都找不到原因所在。你愿意
當前者還是后者呢?想當前者的就請好好看完本篇文章吧。文中涉及較多Gif演示動畫請注意。
前言:任何一個編程者都少不了要去調試代碼,不管你是高手還是菜鳥,調試程序都是一項必不可少的
工作。一般來說調試程序是在編寫代碼之后或測試期修改Bug 時進行的,往往在調試代碼期間更加能夠
體現出編程者的水平高低以及分析問題的準確度。不少初學者在尋找錯誤原因時,總是不得要領,花費
了大量時間卻無法解決一些最終證明是相當簡單的Bug。掌握各種調試技巧,必定能在工作中起到事半功
倍的效果。譬如,快速定位問題、降低故障概率、幫助分析邏輯錯誤等等。而在互聯網前端開發越來越
重要的今天,如何在前端開發中降低開發成本,提升工作效率,掌握前端開發調試技巧尤為重要。
本文將一一講解各種前端JS調試技巧,也許你已經熟練掌握,那讓我們一起來溫習,也許有你沒見過的
方法,不妨一起來學習,也許你尚不知如何調試,趕緊趁此機會填補空白。
1、骨灰級調試大師Alert
那還是互聯網剛剛起步的時代,網頁前端還主要以內容展示為主,瀏覽器腳本還只能為頁面提供非常簡
單的輔助功能的時候。那個時候,網頁主要運行在以IE6為主的瀏覽器中,JS的調試功能還非常弱,只能
通過內置于Window對象中的alert方法來調試,那時候看起來應該是這個樣子:
需要說明一點,這里看到的效果,并非當年的IE瀏覽器中看到的效果,而是在高版本IE中的效果。此外
,當年貌似還沒有這么高級的控制臺,而alert的使用也是在真實的頁面JS代碼中。雖然,alert的調試
方式很原始,但當時確實有它不可磨滅的價值,甚至到今天,已然有其用武之地。
2、新一代調試王者Console
隨著JS在Web前端中能做的事情越來越多,責任越來越大,而地位也越來越重要。傳統的alert調試方式
已經漸漸不能滿足前端開發的種種場景。而且alert調試方式彈出的調試信息,那個窗口著實不太美觀,
而且會遮擋部分頁面內容,著實有些不太友好。
另一方面,alert的調試信息,必須在程序邏輯中添加類似"alert(xxxxx)"這樣的語句,才能正常工作,
并且alert會阻礙頁面的繼續渲染。這就意味著開發人員調試完成后,必須手動清除這些調試代碼,實在
有些麻煩。
所以,新一代的瀏覽器Firefox、Chrome,包括IE,都相繼推出了JS調試控制臺,支持使用類
似"console.log(xxxx)"的形式,在控制臺打印調試信息,而不直接影響頁面顯示。以IE為例,它看起來
像這樣:
好吧,再見丑陋的alert彈出框。而以Chrome瀏覽器為首的后起之秀,為Console擴展了更豐富的功能:
你以為這樣就滿足了?Chrome開發團隊的想象力實在不得不讓人佩服:
好了,稍微多說了一點點題外話。總之,控制臺以及瀏覽器內置Console對象的出現,給前端開發調試帶
來了極大的便利。
有人會問,這樣的調試代碼不一樣需要在調試完成后進行清理嗎?
關于這個問題,如果在使用console對象之前先進性存在性驗證,其實不刪除也不會對業務邏輯造成破壞
。當然,為了代碼整潔,在調試完成后,還是應盡可能刪除這些與業務邏輯無關的調試代碼。
3、JS斷點調試
斷點,調試器的功能之一,可以讓程序中斷在需要的地方,從而方便其分析。也可以在一次調試中設置
斷點,下一次只需讓程序自動運行到設置斷點位置,便可在上次設置斷點的位置中斷下來,極大的方便
了操作,同時節省了時間。——百度百科
JS斷點調試,即是在瀏覽器開發者工具中為JS代碼添加斷點,讓JS執行到某一特定位置停住,方便開發
者對該處代碼段的分析與邏輯處理。為了能夠觀察到斷點調試的效果,我們預先隨意準備一段JS代碼:
代碼很簡單,就是定義一個函數,傳入兩個數,分別加上一個亂七八糟的隨機整數后,再返回兩個數的
總和。以Chrome開發者工具為例,我們來看一下JS斷點調試的基本方法。
3.1、Sources斷點
首先,測試代碼中我們通過上圖console的輸出結果可以看出代碼應該是正常運行了,但是為什么是應該
呢?因為函數中加了一個隨機數,而最終結果是否真的是正確的呢?這是毫無意義的猜想,但是假設我
現在就是要驗證一下:函數傳入的兩個數、被加的隨機數,以及最終的總和。那么該怎么操作呢?
方法一,前面講過最普通的,無論使用alert還是console,我們可以這么來驗證:
從上圖發現,我們在代碼中新增了三行console代碼,用以打印我們關心的數據變量,而最終我們從控制
臺(Console面板)中的輸出結果,可以很清楚的驗證整個計算過程是否正常,進而達到我們題設的驗證
要求。
方法二,方法一的驗證過程存在很明顯的弊端就是,添加了很多冗余代碼,接下來我們看一下使用斷點
進行驗證,是否更加方便,先看一個如何加斷點,以及斷點后是什么效果:
如圖,給一段代碼添加斷點的流程是"F12(Ctrl + Shift + I)打開開發工具"——"點擊Sources菜單"
——"左側樹中找到相應文件"——"點擊行號列"即完成在當前行添加/刪除斷點操作。當斷點添加完畢后
,刷新頁面JS執行到斷點位置停住,在Sources界面會看到當前作用域中所有變量和值,只需對每個值進
行驗證即可完成我們題設驗證要求。
那問題來了,仔細的朋友會發現當我的代碼執行到斷點的時候,顯示的變量a和b的值是已經進行過加法
運算后的,我們看不到調用sum函數時初始傳入的10和20。那么該怎么辦呢?這就要回過頭來先學習一下
斷點調試的一些基礎知識了。我們打開Sources面板后其實會在界面中看到如下內容,我們跟著鼠標軌跡
逐一看看都是什么意思:
從左到右,各個圖標表示的功能分別為:
Pause/Resume script execution:暫停/恢復腳本執行(程序執行到下一斷點停止)。
Step over next function call:執行到下一步的函數調用(跳到下一行)。
Step into next function call:進入當前函數。
Step out of current function:跳出當前執行函數。
Deactive/Active all breakpoints:關閉/開啟所有斷點(不會取消)。
Pause on exceptions:異常情況自動斷點設置。
到此,斷點調試的功能鍵介紹得差不多了,接下來我們就可以一行一行去看我們的程序代碼,查看每一
行執行完畢之后,我們各個變量的變化情況了,如下圖所示:
如上,我們可以看到a、b變量從最初值,到中間加上隨機值,再到最后計算總和并輸出最終結果的整個
過程,完成題設驗證要求不在話下。
其余幾個功能鍵,我們稍微改動一下我們的測試代碼,用一張gif圖來演示他們的使用方法:
這里需要注意一點,直接在代碼區打印變量值的功能是在較新版本的Chrome瀏覽器中才新增的功能,如
果你還在使用較老版本的Chrome瀏覽器,可能無法直接在斷點的情況下查看變量信息,此時你可以將鼠
標移動到變量名上短暫停頓則會出現變量值。也可以用鼠標選中變量名稱,然后右鍵"Add to watch"在
Watch面板查看,此方法同樣適用于表達式。此外,你還可以在斷點情況下,切換到Console面板,直接
在控制臺輸入變量名稱,回車查看變量信息。該部分比較簡單,考慮篇幅問題,不在做圖演示。
3.2、Debugger斷點
所謂的Debugger斷點,其實是我自己給它命名的,專業術語我也不知道怎么說。具體的說就是通過在代
碼中添加"debugger;"語句,當代碼執行到該語句的時候就會自動斷點。接下去的操作就跟在Sources面
板添加斷點調試幾乎一模一樣,唯一的區別在于調試完后需要刪除該語句。
既然除了設置斷點的方式不一樣,功能和Sources面板添加斷點效果一樣,那么為什么還會存在這種方式
呢?我想原因應該是這樣的:我們在開發中偶爾會遇到異步加載html片段(包含內嵌JS代碼)的情況,
而這部分JS代碼在Sources樹種無法找到,因此無法直接在開發工具中直接添加斷點,那么如果想給異步
加載的腳本添加斷點,此時"debugger;"就發揮作用了。我們直接通過gif圖看看他的效果:
4、DOM斷點調試
DOM斷點,顧名思義就是在DOM元素上添加斷點,進而達到調試的目的。而在實際使用中斷點的效果最終
還是落地到JS邏輯之內。我們依次來看一下每一種DOM斷點的具體效果。
4.1、當節點內部子節點變化時斷點(Break on subtree modifications)
在前端開發越來越復雜的今天,前端JS代碼越來越多,邏輯越來越復雜,一個看似簡單的Web頁面,通常
伴隨著大段大段的JS代碼,涉及諸多DOM節點增、刪、改的操作。難免遇到直接通過JS代碼很難定位代碼
段的情況,而我們卻可以通過開發者工具的Elements面板,快速定位到相關DOM節點,這時候通過DOM斷
點定位腳本就顯得尤其重要了。具體我們還是通過gif演示來看一下吧:
上圖演示了對ul子節點(li)的增加、刪除以及交換順序操作觸發斷點的效果。但需要注意的是,對子
節點進行屬性修改和內容修改并不會觸發斷點。
4.2、當節點屬性發生變化時斷點(Break on attributes modifications)
另一方面,由于前端處理的業務邏輯越來越復雜,對一些數據的存儲依賴越來越強烈,而將臨時數據存
儲于DOM節點的(自定義)屬性中,是很多情況下開發者優先選擇的方式。特別是在HTML5標準增強自定
義屬性支持(例:dataset、data-*之類)之后,屬性設置應用越來越多,因此Chrome開發者工具也提供
了屬性變化斷點支持,其效果大致如下:
此方式同樣需要注意,對子節點的屬性進行任何操作也不會觸發節點本身的斷點。
4.3、當節點被移除時斷點(Break on node removal)
這個DOM斷點設置很簡單,觸發方式很明確——當節點被刪除時。所以通常情況應該是在執
行"parentNode.removeChild(childNode)"語句的時候使用此方式。此方式使用不多。
前面介紹到的基本上是我們在日常開發中經常用到的調試手段,運用得當它們也幾乎能應對我們日常開
發中的幾乎所有問題。但是,開發者工具還考慮到了更多的情況,提供更多的斷點方式,如圖:
5、XHR Breakpoints
這幾年前端開發發生了翻天覆地的變化,從當初的名不見經傳到如今的盛極一時,Ajax驅動Web富應用,
移動WebApp單頁應用風生水起。這一切都離不開XMLHttpRequest對象,而"XHR Breakpoints"正是專為異
步而生的斷點調試功能。
我們可以通過"XHR Breakpoints"右側的"+"號為異步斷點添加斷點條件,當異步請求觸發時的URL滿足此
條件,JS邏輯則會自動產生斷點。演示動畫中并沒有演示到斷點位置,這是因為,演示使用的是jQuery
封裝好的ajax方法,代碼已經過壓縮,看不到什么效果,而事實上XHR斷點的產生位置是"xhr.send()"語
句。
XHR斷點的強大之處是可以自定義斷點規則,這就意味著我們可以針對某一批、某一個,乃至所有異步請
求進行斷點設置,非常強大。但是,似乎這個功能在日常開發中用得并不多,至少我用得不多。想想原
因大概有兩點:其一,這類型的斷點調試需求在日常業務中本身涉及不多;其二,現階段的前端開發大
多基于JS框架進行,最基本的jQuery也已經對Ajax進行了良好封裝,極少有人自己封裝Ajax方法,而項
目為了減少代碼體積,通常選擇壓縮后的代碼庫,使得XHR斷點跟蹤相對不那么容易了。
6、Event Listener Breakpoints
事件監聽器斷點,即根據事件名稱進行斷點設置。當事件被觸發時,斷點到事件綁定的位置。事件監聽
器斷點,列出了所有頁面及腳本事件,包括:鼠標、鍵盤、動畫、定時器、XHR等等。極大的降低了事件
方面業務邏輯的調試難度。
演示實例演示了當click事件被觸發時和當setTimeout被設置時的斷點效果。實例顯示,當選中click事
件斷點之后,兩個按鈕的被點擊時都觸發了斷點,而當setTimeout被設置時,"Set Timer"斷點被觸發。
調試,是在項目開發中非常重要的環節,不僅可以幫助我們快速定位問題,還能節省我們的開發時間。
熟練掌握各種調試手段,定當為你的職業發展帶來諸多利益,但是,在如此多的調試手段中,如何選擇
一個適合自己當前應用場景的,這需要經驗,需要不斷嘗試積累。
寫到這里,基本上可以說是傾囊而出了,希望能引起你的注意,希望能夠讓你感到一絲的觸動,感到一
些似曾相識。最主要的我還是希望你能夠快速提高自己的技能,讓自己成為技術牛人!
========
JS高級調試技巧:捕獲和分析 JavaScript Error詳解
前端工程師都知道 JavaScript 有基本的異常處理能力。我們可以 throw new Error(),瀏覽器也會在
我們調用 API 出錯時拋出異常。但估計絕大多數前端工程師都沒考慮過收集這些異常信息
反正只要 JavaScript 出錯后刷新不復現,那用戶就可以通過刷新解決問題,瀏覽器不會崩潰,當沒有
發生過好了。這種假設在 Single Page App 流行之前還是成立的。現在的 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.addEventListener 或 window.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 就是由我們控制的。基本上我們把什么放
進 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 上,不同源是很正常的。現在就算是自己做個小網站,
常見框架如 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>
我們需要實現 scheduleRemoteScript 和 scheduleInlineScript 這兩個函數,并且保證它們在第一個
引用外部腳本文件的 <script> 標簽之前就被定義好,然后余下的 <script> 標簽都會被改寫成上面這
種形式。注意原本立即執行的 step2 函數被放到了一個更大的 code 函數里面了。code 函數并不會被
執行,它只是一個容器而已,這樣使得原本 step2 的代碼不需要轉義就能保留下來,但又不會被立即執
行。
接下來我們還需要實現一套完整的機制,保證這些由 scheduleRemoteScript 根據地址下載回來的文件
內容和由 scheduleInlineScript 直接獲取到的代碼能夠按照正確的順序一個接一個地執行。詳細的代
碼我就不在這里給出了,大家有興趣可以自己去實現。
行號反查
通過 CORS 獲取內容再把代碼注入頁面能夠突破安全限制,但會引入一個新的問題,那就是行號沖突。
原本通過 error.script 可以定位到唯一的腳本文件,再通過 error.line 可以定位到唯一的行號。現
在由于都是頁面內嵌的代碼,多個 <script> 標簽并不能通過 error.script 來區分,然而每一個?
<script> 標簽內部的行號都是從 1 算起的,結果就導致我們無法利用異常信息定位錯誤所在的源代碼
位置。
為了避免行號沖突,我們可以浪費一些行號,使得每一個 <script> 標簽中有實際代碼所使用的行號區
間互相不重疊。舉個例子來說,假設每個 <script> 標簽中的實際代碼都不超過 1000 行,那么我可以
讓第一個 <script> 標簽中的代碼占用第 1–1000 行,讓第二個 <script> 標簽中的代碼占用第 1001
–2000 行(前面插入 1000 行空行),第三個 <script> 標簽種的代碼占用第 2001–3000 行(前面插
入 2000 行空行),以此類推。然后我們使用 data-* 屬性記錄這些信息,便于反查。
<script
?data-src="http://cdn.com/step1.js"
?data-line-start="1"
>
?// code for step 1
</script>
<script data-line-start="1001">
?// '\n' * 1000
?// code for step 2
</script>
<script
?data-src="http://cdn.com/step3.js"
?data-line-start="2001"
>
?// '\n' * 2000
?// code for step 3
</script>
經過這樣處理后,如果一個錯誤的 error.line 是 3005 的話,那意味著實際的 error.script 應該是?
'http://cdn.com/step3.js',而實際的 error.line 則應該是 5。我們可以在之前提到的 reportError?
函數里面完成這項行號反查工作。
當然,由于我們沒辦法保證每一個腳本文件只有 1000 行,也有可能有些腳本文件明顯小于 1000 行,
所以其實不需要固定分配 1000 行的區間給每一個 <script> 標簽。我們可以根據實際腳本行數來分配
區間,只要保證每一個 <script> 標簽所使用的區間互不重疊就可以了。
crossorigin 屬性
瀏覽器對于不同源的內容進行的安全限制當然不僅限于 <script> 標簽。既然 XMLHttpRequest 可以通
過 CORS 來突破這個限制,為什么直接通過標簽引用的資源就不可以呢?這當然是可以的。
針對 <script> 標簽引用不同源腳本文件的限制同樣作用于 <img> 標簽引用不同源圖片文件。如果一個?
<img> 標簽是不同源的話,一旦在 <canvas> 繪圖時用到了,該 <canvas> 將變為只寫狀態,保證網站
不能通過 JavaScript 竊取未授權的不同源圖片數據。后來 <img> 標簽通過引入 crossorigin 屬性解
決了這個問題。如果使用 crossorigin="anonymous",則相當于匿名 CORS;如果使用 `crossorigin=“
use-credentials”,則相當于帶認證的 CORS。
既然 <img> 標簽能這樣做,為什么 <script> 標簽就不能這樣做?于是瀏覽器廠商就為 <script> 標簽
加入了同樣的 crossorigin 屬性用于解決上述安全限制問題。現在 Chrome 和 Firefox 對這個屬性的
支持是完全沒有問題的。Safari 則會把 crossorigin="anonymous" 當做 crossorigin="use-
credentials" 處理,結果是如果服務器只支持匿名 CORS 則 Safari 會當做認證失敗。由于 CDN 服務
器出于性能的考慮被設計為只能返回靜態內容,不可能動態的根據請求返回認證 CORS 所需的 HTTP?
Header,Safari 相當于不能利用此特性來解決上述問題。
總結
JavaScript 異常處理看起來很簡單,跟其它語言沒什么區別,但真的要把異常都捕獲了然后對屬性做分
析,其實還不是那么容易的事情。現在盡管有一些第三方服務提供捕獲 JavaScript 異常的類 Google?
Analytics 服務,但如果要弄明白其中的細節和原理還是必須自己親手做一次。
========
靈活應用js調試技巧解決樣式問題的步驟分享
在很多時候,前端開發人員使用JS腳本,對頁面Dom結構或者是樣式做出了修改,會造成一些意想不到的
bug
由于種種原因,例如:代碼邏輯復雜、時間久了遺忘處理細節、或者根本就是接手修改別人遺留的bug,
在這種情況下,debug就會變成一件頭疼的事情。?
在此分享一些JS調試方面的技巧,針對各種疑難雜癥,往往能起到較好的效果。?
Step 1:檢查服務器直接render出來的內容?
使用查看源文件的方式,這一步首先明確,頁面HTML片段是否在服務器端就已經不正常了。?
Step 2:比較實際的HTML內容和服務器render出來的原始內容之間的差異?
可以使用一些前端工具(例如:IE下的開發人員工具、Firebug、Chrome的開發人員工具等),
實時查看當前HTML片段內容?
Step 3:在合適的位置增加debugger?
例如:先找出大概可能出問題的js代碼,在合適的地方加debugger,或者是使用工具增加類似
于“在屬性被修改時中斷”的斷點,例如實際的HTML比原始內容多出了一個width屬性?
Step 4:運行你的頁面,進入中斷,并檢查js調用堆棧(關鍵的一步)?
推薦使用IE支持的Visual Studio調試器(需要先設置IE:取消“禁用腳本調試”),這時基本
就能確定是哪個js方法修改了樣式
========
總結
以上是生活随笔為你收集整理的js断点和调试学习总结3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: codesmith学习总结
- 下一篇: VS 2010 IDE 宏学习总结