微軟的Javascript代碼規範

<本方譯自http://www.asp.net/ajaxlibrary/act_contribute_codingStandards_Programming.ashx。原文主要針對ASP.net的Ajax 控件工具箱,但多數準則亦普遍適用於javascript編程>

本節的主題是幫助程序員防止一些常見的錯誤,或者強調一些可以明顯提高性能、可維護性或者可支持性的慣例。

5.1  變量聲明

所有變量都應該使用var來聲明,以減少遍歷作用域鏈(scope chain)的額外開銷。除非絕對必要,不要使用全局變量,因爲它們查找起來總是最慢的。變量應該被初始化爲缺省值或者null。例:

var counter = 0; 

var empty = null; 

建議將所有變量聲明都放在函數頭部。將變量分組聲明以減少腳本的大小。如:

var counter = 0, empty = null;

Javascript沒有塊作用域的概念;因此定義在函數內部的"if"或"for"塊中的變量,可以在該函數內的任意地方訪問。因此,對javascript來說是享受不到任何變量延遲定義的好處的。

<譯註:

當必須使用全局變量時,可以使用Global Import  模式,將全局變量導入成爲局部變量,這樣在需要使用原全局變量的時候改爲使用導入的局部變量,從而減少了作用域鏈查找的代價。

在變量分組聲明上,Dojo的觀點是,只能將邏輯上相關聯的一組變量放在一起聲明。如果沒有邏輯上的關聯,則變量必須每個單獨成行聲明。可讀性高於一切。畢竟,這種做法並不會顯著提升性能。>

避免使用"with"語句。它不只是降低了代碼的可讀性,而且對性能亦有損傷。

<譯註:在VBA中使用with語句倒是加快性能的方法。此一時,彼一時也。不過此前根本就不知道javascript還支持with語法>

5.2 函數快捷方式

<譯註: 不理解何爲function shourtcuts。 疑爲IE獨門絕技?>

Avoid using function shortcuts such as $get within script that must coexist on a page with other scripts, or may do in the future. This is to avoid other scripts reassigning a global shortcut to an alternative function, which may cause your script to fail.
Instead use fully qualified function calls whenever a script may be reused in uncertain or shared circumstances, or for non-global functions use a closure as described in section 3.7.1.

5.3  函數別名

當需要在一個循環中調用某個函數多次時,可以使用函數別名以減少作用域鏈的查找,例:

function doSomething() {
    var get = getDataByIndex;
    for (var counter = 0; counter < 10000; counter++) {
        var current = get(counter);
        // ...code removed  
   }
} 

注意在使用別名時,函數一定要設計成允許這樣使用。比如依賴於"this"的函數可能就不能被成功地通過別名來引用。

5.4  比較和相等

任何時候只要可能,都使用嚴格相等符號("===")來決定兩個值是否相等。這樣可以避免隱藏的類型轉換,而且確保精度的責任也不由程序員來承擔。例:

如果"x"和"y"都有數值型值:

錯誤: if (x==5 && y != 4)

正確: if (x === 5 && y !== 4)

5.5  Undefined和null

不要在使用undefined和null關鍵字上搖擺不定,確保任何字段或變量都初始化爲確定值或者爲null;

this._result = null;

如果不象上面這樣聲明,意味着_result將是undefined。

當測試一個類實例變量是否有值,或者一個可選參數是否提供給一個函數時,且該變量/參數確定爲一個對象是,使用下面的語法:

    if (message) {
        // message was provided and not null  
    } 

5.6  循環和遞歸

循環和遞歸都會放大低性能代碼的影響力。因此必須從一開始就堅持好的慣例,而且在必要時進行優化。一些好的編程習慣如:

將一些代價昂貴的調用從重複代碼中移到外層來。如不變的try/catch塊和if-else分支就應該移到外層去。

錯誤:

    for (var counter = 0; counter < 10000; counter++) {
            try {
                performAction();
            } catch (e) {
                alert('Failure: ' + e);
                break;
            }
        }  

正確:

    try {
        for (var counter = 0; counter < 10000; counter++) {
            performAction();
            }
    } catch (e) {
            alert('Failure: ' + e);
        } 

如果退出條件的計算很昂貴,也將它移出來。

錯誤:

    try {
        for (var counter = 0; counter < 10000; counter++) {
            performAction();
            }
    } catch (e) {
            alert('Failure: ' + e);
        }  

正確:

var target = document.getElementsByTagName('div').length;
for (var counter = 0; counter < target; counter++) {
    performAction();
} 

在上面的例子中,對函數getElementsByTagName的調用會導致每個迭代時都重新計算DOM樹一次。這應該並不是假想中必須完成的行爲。如果將這個調用移出循環的話,也要確保performAction調用不會修改循環的集合。

在同一個函數的多個循環中使用同一個循環變量,以避免多次聲明同一變量。

錯誤:

    for (var counter = 0; counter < 10; counter++) {
        performAction();
        };
    for (var counter = 0; counter < 10; counter++) {
        performOtherAction();
        };  


正確:

var counter;
for (counter = 0; counter < 10; counter++) {
    performAction();
    };
for (counter = 0; counter < 10; counter++) {
    performOtherAction();
    };

<譯註: 改正後的代碼在第一個循環前即聲明瞭變量var counter,並在後面兩個循環中都使用同一變量。比錯誤的例子小了一次聲明。注意這個改動能提升的性能十分有限,如果這樣做會犧牲代碼的可讀性/可維護性,則顯見得不償失。>

5.7  DOM操作

5.7.1  批量修改

通過Javascript來操作DOM是十分昂貴的,而且爲引起瀏覽器重新生成文檔流。爲減少DOM樹更新和屏幕重繪次數,應該對DOM進行批量修改,一次更新。

避免拼接HTML到文檔元素的innerHTML屬性(例如使用 += 操作符),這會在DOM樹更新之前就產生一次大的字符串拼接。

5.7.2  循環引用

在DOM元素和Javascript對象之間的循環引用會造成一些老舊的瀏覽器的內存泄漏。儘管這一問題現在已大爲好轉,但對internet應用來講仍然必須仔細考慮。請參見"Understanding and Solving Internet Explorer Leak Patterns"

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章