可維護性
編寫可維護性代碼很重要,因爲開發人員的大部分時間都耗在別人編寫的代碼上,很少有時間去編寫新的代碼,開發工作是別人的成果上開展的。那麼可維護性代碼在這個時候就很重要了,能節約開發人員的大量時間。
什麼是可維護性代碼
可維護性代碼需要遵循以下特點:
- 可理解性,其它開發人員能接手代碼,並能理解其意圖和途徑。
- 直觀性,代碼中的內容一看就能明白,不管多麼複雜的操作。
- 可適應性,代碼以一種數據上的變化而不要求完全重寫的方式編寫。
- 可擴展性,考慮到未來,可允許對主要功能進行擴展。
- 可調試性,出現問題,代碼能夠給出確定信息讓開發人員儘快找出問題所在。
代碼約定
讓代碼變得可維護的最簡單的方法就是形成一套JavaScript中書寫約定。
可讀性
可維護性,首先它必須可讀。可讀性的大部分內容跟縮進有關,縮進用空格縮進而不用製表符,因爲製表符在不同的編輯器中有不同的效果,一般縮進採用4個空格爲一縮進。
提高可讀性的另一個方法就是 代碼註釋。它能告知開發人員此處代碼是做什麼的,有什麼意義,方便開發人員進行維護,一般在下列情況下需要代碼註釋:
- 函數和方法,當定義一個函數或者方法,使用代碼註釋,用於描述目的和完成任務所需的算法。
- 大段代碼,完成某單個任務的多行代碼,給一個完成任務的描述。
- 複雜的算法,使用某獨到的算法完成任務,使用註釋可以說明此算法的目的,含意。
- Hack,當需要解決瀏覽器兼容問題時,使用一些Hack技術,書寫註釋,能更好的解釋解決方法的作用。
函數和變量命名
適當地給函數和變量命名是讓代碼可理解性和可維護性的一個有效方法,規則如下:
- 變量名應爲名詞
- 函數名以動詞開頭,如:getName()。返回布爾值的函數以is-開頭。
- 變量和函數必須使用符合邏輯的名字,無需擔心長度問題,後期可以通過壓縮來解決名字長度問題。避免使用無用變量名。
變量類型透明
由於JavaScript變量鬆散性的,所以很容易讓人忘記該變量保存的是什麼類型的數據。所以有三種方法表示變量數據類型的方式。
第一種就是初始化變量,初始化變量後,將來該變量保存的應該就是初始化值的類型。
var a = 1,
b = "hi";
a將來保存的是數字類型值,b將來保存的是字符串類型值。
第二種方法就是在變量的前面加上一個字符來提示該變量保存着什麼類型的值。該字符可以是:n、o、s、b分別表示數字類型、對象類型、字符串類型和布爾類型。
var na = 2,
sw = "hi";
這樣a就表示保存數字類型,w保存着字符類型。
第二種方法就是添加註釋,在變量與賦值符號之間添加註釋,註釋該變量保存着什麼類型的值。
var a /* number */ = 1,
w /* string */ = "hi";
鬆散耦合
所謂的鬆散耦合,就是將HTML和CSS分離,即將內容與樣式分離,將HTML和JavaScript分離,將內容與行爲分離,將CSS和JavaScript儘量分離,將樣式與行爲分離。將CSS文件通過外部引入到頁面中,將JavaScript通過外部引入到頁面中。有時我們需要通過JavaScript動態地爲頁面添加樣式,這時,我們只需用JavaScript更改樣式類即可。
顯示問題唯一來源是CSS,行爲問題唯一來源是JavaScript,在這些層次之間保持鬆散耦合可以讓代碼更容易維護。
編程實踐
尊重對象所有權
不要爲實例或原型添加屬性
不要爲實例或原型添加方法
不要重定義已存在的方法
避免全局量
不要過渡使用全局變量或全局函數。
避免與null進行比較
如果值是一個引用類型,用instanceof檢查其構造函數。
如果值是一個基本數據類型,用typeof檢測屬於哪個類型
性能
隨着JavaScript的誕生,使用這門語言來編輯網頁的開發人員越來越多,相對的,效率低的代碼也越來越多,這有一些方法來改變運行性能。
注意作用域
隨着作用域鏈上作用域的數量的增加,訪問當前作用域外的變量所有時間也在增加,訪問全局作用域總比訪問局部作用域所有的時間多、速度慢,因爲還要遍歷作用域鏈。那麼只有減少在作用域鏈上的時間來提升整體的運行性能。
避免全局查找
優化腳本性能最重要的方法之一就是注意全局查找。使用全局變量和函數比局部變量和函數耗時、開銷大,因爲涉及作用域鏈的查找。
function upDateUI() {
var imgs = document.getElementsByTagName("img");
for (var i = 0, len = imgs.length; i < len; i++) {
imgs[i].title = document.title + "image" + i;
}
var msg = document.getElementById("msg");
msg.innerHTML = "update complete";
}
upDateUI()函數包括了三個對全局document對象的引用,如果頁面中有十張甚至上百張圖片,那麼在for循環中對document對象的引用要執行十次甚至上百次,也就是說要在作用域鏈上查找相同對象上百次,這是一件損耗性能的工作。我們可以創建一個局部變量,使其指向document對象,這樣就可以限制爲一次訪問全局對象document(只執行一次全局查找),也只需在作用域鏈上執行一次全局查找足以。雖然只訪問一次,但這個局部變量是一個指針,指向document對象的,訪問一次就已經查找到document對象了,所以儘管多次調用這個局部變量,document對象卻早已查找到,但此時無需在作用域鏈上查找了,因爲局部變量已經指向這個對象了。
function upDateUI() {
//局部變量指向document對象,訪問一次就已經查找到document對象了。
var obj = document;
var imgs = document.getElementsByTagName("img");
for (var i = 0, len = imgs.length; i < len; i++) {
imgs[i].title = obj.title + "image" + i;
}
var msg = document.getElementById("msg");
msg.innerHTML = "update complete";
}
現在函數只有一次全局查找。
機巧:將 在一個函數中會用到多次的全局變量存儲在一個局部變量中是一個減少損耗的好辦法。
避免with語句
我們知道,with語句有自己的作用域,它會擴充執行環境,所以with語句會增長作用域鏈長度,因此要避免使用with語句。
選擇正確的方法
性能問題的一部分原因來自於算法的複雜程度和方法的複雜程度。
避免複雜的算法
算法的複雜性也會影響到性能,算法越是複雜,那麼損耗就越大,執行代碼所需要的時間就越長。因此,避免太複雜的算法也是降低損耗的一個辦法。
優化循環
- 減值迭代:一般循環是從0開始,增加到某個終止值爲止的迭代器。大多數情況下,從最大值開始,在循環中不斷減值的迭代器更高效。
- 簡化終止條件:也就是終止條件的算法複雜程度要減小。
- 簡化循環體:最大限度地簡化循環體語句。
- 使用後測循環:也就是多用do-while循環語句(避免最初終止條件的計算,也就是第一次循環時,不會計算終止條件)。
展開循環
也就是知道循環次數且循環次數少的情況下,就不用循環,展開來。
例如,我們知道某循環會執行3次即循環次數爲3,傳統書寫爲:
var arr = ["red", "blue", "yellow"];
//減值迭代器
for (var i = arr.length - 1; i >= 0; i--) {
console.log(arr[2 - i]);
}
當我們展開循環爲:
var arr = ["red", "blue", "yellow"];
console.log(arr[0]);
console.log(arr[1]);
console.log(arr[2]);
這樣展開循環可以消除建立循環和處理終止條件的額外開銷。
避免雙重解釋
例如:
eval("alert('hello')");
這個例子會實例化一個新的解析器,這樣就會有開銷,不如直接將代碼提取白出來:
alert("hello");
提升性能的其它方法
這些方法都次要的,不過處理好的話,對提升性能也有所幫助。
- 原生方法快,JavaScript中的原生方法比自已定義方法快,比如常用的Math函數中的屬性,比起自己定義的方法快。
- switch語句快,如果有複雜的if-else語句,可以轉換成單個的switch語句。
- 位運算符比較快。
最小化語句數
減少多個變量聲明
當我們聲明多個變量時,往往用多個var來聲明,
var a = 21;
var b = "hello";
var c = true;
我們可以只用一個var來聲明這些變量:
var a = 21,
b = "hello",
c = true;
使用數組和對象字面量
當我們創建一個數組並初始化時,
var arr = new Array();
arr[0] = 1;
arr[1] = "hello";
arr[2] = true;
可以用數組直接將元素值插入:
var arr = [1, "hello", true];
在創建一個對象並初始化時,
var obj = new Object();
obj.name = "tom";
obj.age = "21";
obj.sayName = function () {
alert(this.name);
};
我們可以用對象字面量法創建一個對象:
var obj = {
name : "tom",
age : 21,
sayName : function () {
alert(this.name);
}
};
優化DOM交互
使用innerHTML
對於小的DOM的更改,createElment()、appendChild()以及innerHTML這三種方法的效率差不多,而對於大的DOM的更改,建議使用innerHTML更好。
DOM交互開銷很大,所以要限制DOM的交互次數。
部署
壓縮文件
壓縮涉及兩個屬性:代碼長度和配重。代碼長度指:瀏覽器所要解析的字節數。配重指:服務器實際傳到瀏覽器的字節數。
壓縮一般有如下操作:
刪除額外的空格(包括換行符)
刪除所有註釋信息
縮短變量名