2012年3月29日

[Web Develop] Debug 小技巧

紀錄一下小小的 Debug 心得
俗話說工欲善其事,必先利其器,
所以我們就先從工具說起,
首先是在開發 Web 程式時,最常用的網頁開發工具
firefox - 以 firebug 為例 (http://getfirebug.com/wiki/index.php/Main_Page)

IE (http://msdn.microsoft.com/en-us/library/hh772704(v=vs.85).aspx)


Chrome (http://code.google.com/intl/zh-TW/chrome/devtools/)




以上三種 Browser 中的 Developer Tool 介面上差異頗大,但其實內容大致上相同,
大致上可以分成
Console(IE譯作主控台)、HTML、Javascript、Network

Console 的部份在之前的文章中有提到,
我們可以利用 console.log(...) 、 console.info(...) 、 console.error(...) 等方法,
將偵錯的資訊印出來,顯示這些訊息的地方就是 Console ,
通常在 Console 中也允許我們用 Command line的方式輸入 Javascript 做運算。

HTML 的部份則是協助我們,將網頁上的元素轉換成他在 HTML 中的位置,
相關的屬性、CSS Style 等等,大都可以在這個地方找到。

Javascript 的部份,通常都允許我們在這邊做 Javascript 除錯,可以下中斷點,
觀察 Javascript 運作的情形。

Network 則是紀錄 Browser 發出什麼 Request 、 Web Server 做出什麼回應,
花了多少時間、以什麼方式傳遞...等資訊。

對這些工具有了大概的瞭解之後,來看看平常如何運用這些工具。

當網頁出現 javascript error 的時候,
我們可以在 Console 裡看到,所有發生 Error 的程式片段,
用滑鼠點擊這些錯誤時,這些工具通常都可以直接跳到 Javascript 中,
並標示出發生 Exception 的程式碼位置。
這個功能對我們在 Javascript 程式除錯上非常有幫助。

下圖示範,當按下畫面中的 Test Button 時,會 throw 一個 exception 出來,
在 Console 的畫面中就可以看到他指出錯誤發生的地方。


當按下上圖中描述錯誤發生點時,就會跳到 Script 的畫面。


下圖是 Network 的畫面,從這個畫面,
可以看出 Browser 對 Server 發出的每個 Request,
以及每個 Request 回應的狀態方式等等。

從上圖每個 Request 可以再進細項到該 Request 的內容,
包含 Request 和 Response 的 Header 、 Cookies 、及實際傳輸的內容都可以看得到,
下圖是在使用 UpdatePanel 發出 AsyncPostback 從 Server 回應的內容,
仔細看內容很不一樣喔,這是因為 UpdatePanel 是部份更新,
更新的方式是透過 Javascript 將 Server 傳回來的容 Parse 後,
再展現到畫面上,所以他並不是一個完整的 HTML 頁面。

再來看看, ScriptManager.RegisterStartUpScript 丟出來的內容長什麼樣子。 

從上圖可以知道,如果 ScriptManager 丟出來的 Script 有錯的話,
很可能會造成 Asp.Net AJAX framework 底層 Parse 錯誤,
導致 Javascript 無法正常執行,而 DevTool 又無法正確指出有問題的程式碼位置。
有可能發生的情況是, Server 端程式 Throw 出來的 Exception 包含有單引號,
或其他 Javascript 的保留符號,但沒有處理到,
這時候就可以用這種方式,看到 Server 到底是 throw 出什麼 exception。

[Refactoring] 愛他就請在最接近第一次使用他的地方宣告他(變數)

變數宣告是不是一定要在函數的開頭?

在 C 的年代,問這種問題應該會被恥笑到抬不起頭,
沒有人去挑戰,因為 Compiler 就告訴你這樣不行(其實是因為 Compiler 這樣比較好做),
而現在, C 的輝皇年代過去了,後繼的程式語言取消了這個限制,
於是我們再來討論一下這個問題:變數宣告是不是一定要在函數的開頭?

就我認為,沒這個必要,因為他一點好處都沒有,
反而會帶來不必要的困擾,常常會看到那種宣告完了,
就再也不用他的變數,因為你並不是為了用他而宣告他,
而是覺得等一下會用,但寫了幾行程式碼之後,就把他忘得一乾二淨了,
但這並不是最惹人厭煩的,

試從以下這種角度思考:
假設函數有 20 行程式碼,我在第一行便宣告了一個變數,
這代表接下來的 19 行程式碼都看得見他,
即使他們一點關系都沒有,如果這中間又做了什麼處理,
要知道這個變數最終的結果,或者這變數會影響什麼,
你都無法忽略這 19 行程式碼,
那如果是在第 10 行宣告呢?
這表示前 10 行程式和這變數一點關系都沒有,
你只要關心剩下來的 10 行程式,多開心ㄚ。

也許你會說,那全域變數怎麼辦,但,別忘了,我這裡說的是函數中的區域變數,
既然談到了全域變數,那就來探討一下全域變數,
就我的觀點,是能不用就不用,從上面的角度思考,
假設程式有 1000 行,我在第一行便宣告了一個變數,
這表示接下來的 999 行程式碼都可以看到他,
方便是方便,但這也表示如果這個變數有問題,
你得看完這 999 行程式碼,也許你又會說,
可我只有在一兩個地方用到他,所以其實我只要看那一兩個地方,
除非你的程式很小,一般情況,你會很快的忘了你只有在一兩個地方用過他,
更甚者在多人開發的情況下,沒人會知道只在一兩個地方用他,
甚至有人還會參一腳,把他拿去用,而這些情況都會讓你的程式耦合度變高,

所以,我主張,變數的能見範圍,最好是夠用就好,
像 Code Complete 書上說的,在最接近第一次使用他的地方宣告他即可,
別再偏執的把區域變數都集中在一塊宣告了

PS. 全域變數,如果真的要用,那還是集中在一起比較好,
因為你可能會想知道他們各自的初始值是什麼,
但,聽我個忠告,能不用就別再用他會對你比較好。