如何為你的 jQuery 程式除錯Elijah Manor | 2009 年 11 月 23 日
(你可以在 jsbin.com/opayo/edit 檢視、執行、編輯這段範例的程式碼。文章中的其他範例我也都有提供在上面)。 JavaScript 的 alert 方法相當煩人,不過一直以來,開發人員也沒什麼好工具,可以在重建或解決錯誤時,幫助他們在用戶端的除錯工作。另外,alert 方法用在程式除錯時,也可能導致一些錯誤的假設。舉例來說,一個alert對話視窗在你關閉它之前,會一直停在那裡,所以用它來當除錯機制,有可能會因此錯過一些事件發生。 本篇文章,我會介紹一些除錯技巧,幫助你處理使用 jQuery 在用戶端網頁程式開發的情況。幸好比起 5 年前的開發人員,我們已經有一些更好的工具和技術。 開始入門對我而言,運用 jQuery 的最好方法,就是先在 Firefox 開發,然後在IE測試。雖然還有別的瀏覽器,不過如果你也用這個方法,比起運用其他瀏覽器的組合搭配,需要付出的力氣會少一點。 由於低調 JavaScript(unobtrusive JavaScript)開發概念的影響,也增加應用程式在開發和除錯的難度。使用低調 JavaScript 開發時,它不會將事件程式緊緊綁在 DOM 元素上,而是在 DOM 產生後才綁定事件,而當 JavaScript 被關閉時,它可以優雅降級,不影響程式運作。觀於這點,可以看看下面的範例(jsbin.com/otowo/edit)。
有鑑於大多數的瀏覽器都會開啟 JavaScript,因此低調程式的價值,更在於它分隔 DOM 的外觀和 DOM 的處理。兩者分隔的結果,卻也提高了程式除錯的難度。 必要的工具這裡我介紹的工具多數都在 Firefox 上使用。IE 8 事實上內建一個很優秀的除錯工具,你也可以用來解決文章中強調的一些地方。不過我傾向於在 Firefox 開發後才發生的問題,才用 IE 來解決。下面是文章中會介紹到的工具簡介。在後面的章節中,我會更為詳細地介紹。
選取器除錯jQuery 程式開發的起點,是找到正確的選取器。找到後,你可附加事件、加上動畫等。不過,有時後要精確地找到所需的選取器,並不是那麼容易。幸好有幾個工具和外掛,可以協助你找出正確的選取器。 Firebug 主控台第一個測試選取器的地方,就在 Firebug 的主控台(console)。你可以在主控台視窗中輸入選取器,然後點擊執行(Run),如圖1 (jsbin.com/edito/edit)。
假如你的選取器符合多個 DOM 元素,主控台視窗將會用「jQuery ( a#selectMe elijahmanor.com)」來顯示這些元素或是你自己的選取結果。假如都不符合,你會看到像圖2 的畫面。(jsbin.com/edito/edit)。
FireFinder如果你不知道為什麼沒有如預期地傳回選取結果,你可以使用其他的工具來幫助你。其中一項就是 Firefox Firebug 的外掛 FireFinder。在你輸入選取器後,這個外掛便會在頁面上強調顯示符合的 DOM 元素。範例請見圖3 (jsbin.com/edito/edit)。
假如一開始得到的結果不如預期,你可以退回重設選取器,從較廣泛的範圍選起,再逐步縮小到特定的元素。又或者,你可以輸入你認為的選取器寫法,再慢慢調整到能正確選取結果的語法。 圖4 中 (jsbin.com/unadu/edit),我的選取器一開始是用「div a[href$='.zip'][title*='Elijah']」這樣的語法。目標是找到所有指向zip的連結,並且包含用「Elijah」這個字開頭的 title。讓我們先試著從 FireFinder 中看看選取出來的結果。
看來選取器找出的結果和預期不同。當中 elijah5.zip 這個連結不應該被選出來,因為 title 的屬性中的「Elijah」不是在開頭的值置。在 jQuery 官網做了點研究後發現,我們設定開頭位置的屬性用錯了。正確的語法應該是這樣「div a[href$='.zip'][title^='Elijah']"」。你可以在 FireFinder 中更新選取語法,這樣就會顯示我們想要的結果。 jQuery Trace 外掛另一個我最近用來幫助選取器除錯的工具是 jQuery Trace 外掛。這個外掛將複雜的選取器拆解開來(由左而右),先找到、強調通用的選取結果,然後再逐步聚焦,它會在 DOM 元素上套用越來越深的視覺樣式,到最後找到符合結果時,就呈現為金黃色。這個獨特的方法,可以讓你檢視選取器的過程。當你的語法不正確時,你可以看到動態效果,找出錯誤選取的所在之處。 下面程式碼(jsbin.com/unadu/edit)稍微修改了之前的版本,方便說明這個外掛的獨特功能。
這段程式追蹤「
事件除錯假設問題發生在事件處理器沒有觸發,或是執行時有些毛病。像這種情況發生時怎麼除錯?有幾個技巧這時可以派上用場,我會在下面這些範例中介紹。 檢查事件是否有註冊你的程式沒有跑起來,常見的問題是你把它加到錯誤的事件上,或是把事件處理器加到錯誤的元素。註冊事件的方法,會決定它出現的位置。讓我們先從 Firebug 的命令列開始,然後再看看其他的選擇。 使用 Firebug 命令列第一步你要採取的步驟,也許是找出你的事件處理器,看看是否註冊在正確的事件上。如果你用的是 bind 方法或 jQuery 的事件協助程式(click、hover、keypress 等等),就註冊事件處理器來說,你可以利用 jQuery 的 data 方法,檢視哪一個事件被附加到實際的 DOM 元素上。 下面的程式片段(可查看 jsbin.com/asili/edit),是從 Cody Lindsey 的 jQuery Enlightenment 電子書修改而來,可以在 Firebug 主控台中顯示附加在特定 DOM 元素上的所有事件。
首先,click 和 mouseleave 事件被附在 <p> 標籤上。這裡用了 Firebug 的 console.dir 方法,取出儲存在 DOM 元素 <p> 裡面的事件資訊,然後輸出到主控台視窗中。圖6 (jsbin.com/asili) 可以看到執行後的結果。你也許也會注意到,當你 <p> 元素發生 click 或 mouseover 時,事件的名稱會顯示在主控台上。
假如你使用 live 方法來加入事件處理器,剛剛我說的方式就沒辦法使用,因為事件處理器的資訊沒被 data 方法存入 DOM 元素中。取而代之的是,資訊被儲存在最上層的 DOM 元素 document 上。想要了解關於這方面的資訊,你可以讀 Neeraj Singh 的文章 How live method works in jQuery. Why it does not work in some cases. When to use livequery。這篇文章可以幫助你理解 bind 和 live 方法之間的差異,不過你要記住這裡指的是 jQuery 1.3 的版本。在 1.4 的版本中,live 方法支援所有的事件,文章中提到關於使用 livequery 外掛的部分就不需要了。 下面程式碼(jsbin.com/acihi/edit)顯示 data 方法運用在 bind 和 live 方法上的差異:
程式輸出的結果顯示在圖7 (jsbin.com/acihi/edit)。我複製、貼上和執行 <pre> 中的內容到 Firebug 主控台的命令列中。這裡使用複合的 Firebug 的 group 方法分開邏輯意義上的宣告組合,我也使用了 Firebug 的 dir 方法,顯示互動物件的屬性。
雖然可以顯示 live 事件附加到根元素 document 上面,但是你沒有辦法點擊 Firebug 主控台中綠色的 function 連結來瀏覽事件處理器,不過如果是用 bind 方法就可以。假如你點擊 function 連結,他會試著瀏覽 jQuery 程式庫,而不是你加到 live 方法的函式。雖然你無法瀏覽事件處理器,但還是有個好主意可以看看發生什麼的。假如你在主控台中擴充 live 樹節點,它會列出具體的選取器名稱,以及用 live 方法註冊過的事件型態。選取器和事件資訊是對 live 方法而言是必要的,這樣它才能回應動態建立的 DOM 元素。 使用 Firebug 的 FireQuery 外掛除了使用 Firebug 主控台和命令列來查看事件處理器之外,你可以使用 Firebug 上的 FireQuery 外掛。這個外掛會在 HTML 頁籤(tab)上顯示附加在 DOM 元素的事件。在圖8 中 (jsbin.com/ufezi/edit),你可以看到 HTML 頁籤中顯示 #helloWorld 段落附加了一個 click 事件,用灰底文字顯示在元素的旁邊。
你不只能看到事件,你也可以看到用 jQuery data 方法附加到 DOM 元素上的資訊。你也許注意到了,在 Firebug 的 HTML 頁籤中,可以看到 "Hello Goodbye" 段落有一些額外的資訊。 使用 Firebug 的除錯功能假設你已經知道事件被註冊到正確的元素上,下一步就是檢查它是否如你所想那樣運作。在 Firebug 中有許多選擇,要從哪裡開始有點難,不過就讓我們先從一些你需要知道的狀況開始,然後再找出哪個功能可以幫你把事情做好。 狀況1:我想設一個中斷點,用來檢查 JavaScript 程式的流程和變數值。有幾個方法可以在Firebug中開始你的除錯工作。 1. 在程式碼旁邊設定中斷點 如同你在多數除錯工具看到的一樣,你可以在程式行號旁邊設定中斷點,就像圖9顯示的一樣(jsbin.com/ewavu/edit)。程式執行到中斷點時,除錯工具就會停在你選的那一行程式碼。這個時候,你可以使用所有你希望看到的除錯功能,像是流程控制,堆疊追蹤和監看表達式。
2. 設定 Firebug 發生錯誤時自動中斷 除了錯誤可能發生之處設定中斷點外,你也可以啟用 Firebug 的 Break On All Errors功 能,它可以自動在錯誤發生時進入除錯模式。你可以點選主控台頁籤選單中的暫停按鈕來開啟功能。圖10 就是一個範例(jsbin.com/aciro/edit)。
3. 使用除錯關鍵字 Firebug 一個較不為人知的功能是 debugger 關鍵字,你可以在程式碼中使用 debugger,就如下面的範例一樣(jsbin.com/ugavi/edit)。執行到那行時就會停止,然後你可以做一些平常會做的除錯工作。很顯然你不會想要在上線的程式碼中留著 debugger 關鍵字,不過在開發階段,用來除錯算是很方便的作法。
狀況2:有個事件處理器被呼叫,但我不知道是被哪一個元素觸發。好消息是在監看視窗中,this 關鍵字可以查看是哪個元素觸發事件。圖11(jsbin.com/usumo/edit)中顯示找到 p#helloWorld 元素是觸發 click 事件的元素。
狀況3:在很深的協助函式呼叫中發生錯誤,而我不確定錯誤究竟是從何而起。Firebug 會作堆疊追蹤,所以你可以追蹤函式呼叫的流程到它的源頭。有兩個主要的方法可以完成這個任務。 1. 設定中斷點,檢視堆疊頁籤 追蹤堆疊最簡單的方法,便是在程式中設定中斷點。一旦除錯工具暫停,你就可以在程式碼視窗旁邊的堆疊頁籤中查看。範例就如圖12(jsbin.com/eyaxe/edit)。
如你在圖12 中所見,堆疊顯示了方法執行的順序。由 firstName 方法觸發了 console.trace 方法,而它又是被 fullName 方法調用,而後者則是被匿名事件處理器所呼叫。從這裡開始,堆疊就進入了 jQuery 函式庫的程式碼。 2. 在你的程式碼裡呼叫console.trace 如果不設定中斷點,你也可以在你的方法中加上一點程式,找出堆疊的路徑。下面程式片段(jsbin.com/iduqu/edit)說明我如何從函式中呼叫 console.trace 方法,而它是深藏於數個堆疊函式呼叫之中。
上面程式片段會在 Firebug 主控台視窗中,輸出堆疊追蹤的結果。輸出的結果和之前圖12 一樣,不過這是用程式產生出來,有別於使用中斷點和堆疊視窗。 狀況4:一個事件處理器執行相當久的時間,我需要找出瓶頸在哪裡,並且修正它。Firebug 提供了幾個技巧可以幫助你診斷程式的效能問題。 1. 使用 console.time 方法 你可以將程式碼包進 console.time 、console.timeEnd 之中,找出呼叫這些程式總共花了多少時間。如果你知道哪段程式碼有效能問題,而且好奇它影響有多大,這些資訊會很有幫助。不過這個方法無法依執行時的函式,分別計算所用時間。如果你需要這類細節,你需要使用下一節要介紹的 console.profile 功能。下面程式碼(jsbin.com/aqibe/edit)是使用 console.time 和 console.timeEnd 的例子。
這段程式輸出結果顯示在下面。程式在 10 萬次迴圈中,加總 1 到 10 之間的亂數,總共花了 9 毫秒,數字加總的結果是 500,405。
2. 使用 console.profile 方法 如果你需要程式的統計細節,你該試試 Firebug 的 console.profile 功能。你可以從主控台視窗或程式碼中啟用 profile 功能。讓我們從下面程式碼中示範量測的方法(jsbin.com/ufijo/edit)。
首先,點擊 Firebug 主控台視窗 Profile 按鈕,開始搜集資訊。當你準備要停止搜集資訊時,再按一次 Profile 按鈕就可以。這時,Firebug 就會在主控台視窗顯示統計結果。就像圖13 中所見。(jsbin.com/ufijo/edit)。
如果你需要更精確掌握量測的開始和結束,那還有一個方法可以供你選擇。你可以用 console.profile 以及在結束的地方使用 console.profileEnd,你就可以看見 Firebug 將資訊搜集到主控台視窗中。下面程式執行的結果就如和圖13 一樣(jsbin.com/oqiwa/edit)。
狀況5:你從 Ajax 呼叫中取得回傳的 JSON 資料,不過很難看懂它的內容有個動作我作了一段時間了,就是把 AJAX 回傳的 JSON 複製、貼上,然後把它貼到線上 JSON 格式化工具,檢查看看回傳的資料是否一如預期。後來我意識到,Firebug 就可以為我做這些事了!你可以在 Firebug 裡展開 Ajax 程式呼叫,裡面會包含一個 JSON 頁籤,並且將資料解析成樹狀格式。你可以看圖14 的執行範例。
串接除錯如果你開發 jQuery 已經一段時間了,你應該已經看過也用過將幾個 jQuery 方法串接在一起使用。這樣做不止是效能上的最佳考量,而且也能讓程式碼保持乾淨、簡單。 儘管串接帶來許多好處,在長串連接過程中,程式也變得難以除錯。舉例來說,它很難判定在一連串的連接動作裡,this 的 jQuery 物件究竟指向誰。除了將串接程式拆成小段宣告的作法之外,你可以使用一個由 Dominic Mitchell 撰寫的小型的 jQuery Log 外掛。下面的程式碼(jsbin.com/opaya3/edit)顯示這個外掛內容,以及它執行時的幾個範例。
輸出的結果顯示在圖15(jsbin.com/opaya3/edit),如你所見,你可以在串接中,從不同的層次呼叫 log 方法,它會隨著取選的 jQuery 物件印出 log 訊息。
這個外掛應該可以加點程式強化,讓它也能在不支援 console.log 方法的瀏覽器中使用(例如 Opera 和 Internet Explorer)。而且如果能在全域層次禁用訊息輸出的話,就更好了。 使用條件式中斷點大部分的人也許都已經用過 Firebug 來找出 jQuery 程式中的問題,不過如果需要的情況是,設立一個中斷點,只在某個特殊情況中執行、除錯,而大部分函式執行時都忽略它?在你其實只要某個特殊情況才需要停下來除錯,卻只能看中斷點一次又一次地執行,這過程實在會讓人深感挫折。 Firebug 有條件式中斷點的概念,你可以提供一個布林值,只在條件為真時才中斷執行、打開除錯工具。如果你之前沒有用過這個功能,它能為你省下寶貴的時間。需要做的只是在行號旁邊設定中斷點的地方,按右鍵,輸入你的條件宣告即可。圖16(jsbin.com/ukoru/edit)以前面 console.time 程式片段,說明設定條件式中斷點的方法。這個宣告會在迴圈索引等於 32 的時候,告訴除錯工具暫停程式。
結語隨著新的網頁開發技術越來越強調和使用者的互動,開發人員需要改變他們的除錯方式,以便更快速、更有效地開發和解決問題。 我希望我介紹的這些技巧和訣竅對你現在或之後的專案有幫助。現在已經有許多很棒的工具和功能可以使用,不過有許多開發人員還不知道。而我特別喜愛 Firebug,建議你可以好好認識這個工具。 假如你有心想持續學習 jQuery,你可以加入我的 Twitter 帳號,我會提供一些 jQuery 相關的連結。另外,你也可以看看我的部落格每天更新的 Tech Tweets 系列文章,那裡也會提供一些幫助你學習 jQuery 的資訊。 |
![]() Elijah Manor is a Christian and a family man. He develops at the Sommet Group using ASP.NET MVC and jQuery. He is an ASP.NET MVP, ASPInsider, and a co-host of the Official jQuery Podcast. He is also active on Twitter and provides daily up-to-date Tech Tweets. Find Elijah on:
More from Elijah:
Videos
Articles
|