轉自老田的部落格 《提高Javascript程式的魯棒性》Posted in 2009/07/10 02:17h. yongbin
我是學控制理論出身,一個控制系統的魯棒性,確實是在做系統設計時需要著重注意的。什麼是控制系統的魯棒性?
所謂“魯棒性”,是指控制系統在一定(結構,大小)的參數攝動下,維持某些效能的特性(摘自百度百科……汗)。
在軟體設計和開發中,同樣也要時刻考慮提高魯棒性。那麼如何提高JavaScript程式的魯棒性呢?我認為,RIA的魯棒性,一般來講,主要跟js的代碼規範性和DOM操作有關係。下面我總結幾點開發中需要注意的地方,讓頁面跑不死、不報錯。這些事情可能在實際開發中都是小事,但是對於提高軟體整體的魯棒性,是非常critical的!
1. 嚴禁使用全域變數
唉,這個我可是親身體會並深受其害啊!話說,當時我修改一個頁面,開發一段指令碼,需要寫cookie。我發現頁面上以前有人引用了一個js檔案,裡面有setCookie函數,好啊,拿來用!結果這個cookie死活都寫不對。檢查了半天,才發現這個頁面引用的另外一個js檔案也有一個setCookie函數,函數的參數和剛才那個不一樣,而我調用的估計是這個函數!應該是之前的兩個同事在同一個頁面上自己搞自己的,結果竟然函數重名,而這兩個函數都是全域的!設想一下,加入兩個人都在全域範圍內搞一個var i = 0; 那豈不是更加瘋掉。
這個故事得到的教訓,首先當然是同一個team的開發人員應該多相容並包,團結協作。其次呢,頁面的js較多的時候,一定要有一個好的層次,避免全域變數。避免全域變數的一個方式是namespace。例如建立一個這樣的程式結構:
1: var my_project = {
2: dom : {
3: get : function() {},
4: make : function() {} // etc.
5: },
6: cookie : {
7: get : function() {},
8: set : function() {},
9: remove : function() {}
10: },
11:
12: util : {
13: // some project utilities
14: }
15: }
2. 多使用短路運算式
全域變數的部分說的有點多了,大概是因為老田我經曆過那類痛苦吧。短路運算式就是&&和||,在這裡用來提高魯棒性。舉兩個例子,第一,要得到某div節點的第一個節點的nodeName:
1: var div = document.getElementById("div_id");
2: var node_name;
3: if (div && div.firstChild) { // 在用firstChild前,先判斷div是否存在
4: node_name = div.firstChild.nodeName;
5: }
其次,用||來快速解決一些瀏覽器安全色性問題:
1: // 得到元素內文本的跨瀏覽器解決方案
2: var text = span.innerText || span.textContent || "";
再例,用||來設定預設值:
1: // 設定預設值
2: var my_attr = span.getAttribute("my_attr") || default_value;
3. 操作元素前,首先判斷其存在
在第二條裡面已經舉過例子。再舉一例:
1: var input = document.getElementsByName("input")[0];
2: if (input) {
3: target = input.value;
4: }
4. 良好的代碼縮排和格式
Doug Crockford,《JavaScirpt: The Good Parts》的作者,在給Google做演講的時候,詳細分析了代碼格式的重要性。比如:
1: return
2: {
3: name : span.nodeName,
4: value : span.innerHTML
5: }
寫這種代碼的人肯定是C出身。js裡面,後果就是return nothing。js有語句的自動補全機制,return這一樣,會自動在末尾加上分號,導致返回。如果不注意的話,程式出了問題,很難找到原因。所以一定要養成一個代碼格式的好習慣,把左大括弧和return啊,if啊,function()啊,寫在一起。也就是:
1: return {
2: // something
3: }
5. 使用跨瀏覽器的代碼
在對DOM進行讀取和操作時,處處想著瀏覽器安全色性這件事,免得最後返工,自己給自己找麻煩。比如,只用document.getElementById;用nodeName,而不用tagName;不用getElementsByClassName等先進的選取器;不用applyElement, removeNode, swapNode等IE only的方法。
在瀏覽器安全色性方面,強烈推薦ppk的網站quirksmode.org。ppk此人對各個瀏覽器都進行了測試,工作十分紮實,是我們的榜樣啊!
6. 變數儘早定義
JavaScript的變數範圍是所在的整個function,在寫代碼的時候,如果變數在想用的時候再定義,很容易變得難於管理。在開發邏輯比較複雜的程式時,非常容易混亂,甚至變數重名。
7. 小心NodeList
NodeList,也就是節點列表,是一個陷阱,他不是靜態,而是危險的動態結構,它會立即反映文檔的變化。一個經典的死迴圈案例:
1: var lis = ul.getElementsByTagName("li");
2: for (var i = 0; i < lis.length; i++) {
3: ul.appendChild(lis[i].cloneNode(true));
4: }
上面的代碼裡,這些li的length不是固定的,而是隨著DOM的變化而變化。這個錯誤我是經曆過一次啊,不過不是死迴圈。還拿這個ul和li舉例子吧,我想刪掉某個ul下面的所有li,代碼類似於下面的樣子,大家看看有什麼問題呢?
1: var lis = ul.getElemenetsByTagName("li");
2: for (var i = 0; i < lis.length; i++) {
3: ul.removeChild(lis[i]);
4: }
8. 使用===和!==
某些人可能會問,我用==用的挺好的啊,為什麼要用===?那我要反問,既然===更加嚴謹,沒有類型轉換,而==充滿陷阱,為什麼不用===?沒有自信嗎?哦,那隻能說明你的程式不穩定,魯棒性不好了。
==會進行自動類型轉換,Crockford稱它為evil twins。其他的不說了,舉幾個例子:
1: alert(0 == ''); // true
2: alert(false == '0'); //true
3: alert('\r\n' == 0); // true
9. 不要用with
在設計JavaScript這門語言的時候,with本來是為一些操作提供捷徑的,但是設計的不夠好,可以說js裡面沒有with會更好。舉一個使用with的例子:
1: with(element.style) {
2: position = "absolute";
3: left = "100px";
4: top = "100px";
5: }
首先with破壞了這門語言的變數範圍結構。在js裡面,postion, left和top本來應該變成全域變數的。可能有些人說,這樣正好,我可以通過with來控制變數範圍!想法不錯,JavaScript 1.7也引入了let語句來做這件事。但是,with會產生無法預想的後果,舉例:
1: var a = '';
2: var obj = {b: 'b'};
3: with(obj) {
4: a = b;
5: }
6: alert(a); // result: 'b'
這裡的a又變成外部變數了!除了這種詭異的事情之外,with語句的執行速度相對來說是非常緩慢的。所以結論就是,想要提高程式的魯棒性,就不要用with。
10. 不要用eval
eval可以把一段字串當作js代碼來執行。eval經常被一些新手拿來用,例如:
eval("value = obj." + key + ";");
其實他完全可以:
value = obj["key"];
eval執行的時候會啟用js解譯器來執行這一小片代碼,速度會慢很多。除此之外,程式的可調試性、可維護性大大降低。類eval的語句還有:
1: window.setTimeout("var i = 0", 1000);
2: var sum = new Function("x", "y", "return x+y");