JS學習筆記(第24章)(最佳實踐)

一、可維護性

1.1 可維護代碼特徵

1. 可理解性
2. 直觀性
3. 可適應性
4. 可擴展性
5. 可調試性php

1.2 代碼約定

一、可讀性(代碼縮進和代碼註釋)
二、變量和函數命名算法

  • 變量名應該爲名詞
  • 函數名應該以動詞開始
  • 變量和函數都應使用合乎邏輯的名字,不要擔憂長度。

三、變量類型透明
(1)第一種方式是初始化,初始化爲一個特定的數據類型能夠很好的指明變量的類型;編程

//經過初始化指定變量類型
var found = false;    //布爾型
var count = -1;       //數字
var name = "";        //字符串
var person = null;    //對象
  • 缺點在於沒法用於函數聲明中的函數參數。

(2)第二種方式是使用匈牙利標記法來指定變量的類型。匈牙利標記法在變量名以前加上一個或多個字符來表示數據類型。數組

//用於指定數據類型的匈牙利標記法
var bFound = false; //布爾型
var iCount = -1;    //數字
var sName = "";     //字符串
var oPerson = null;  //對象
  • 缺點在於讓代碼某種程度上難以閱讀,阻礙了沒有用它時代碼的直觀性和句子式的特徵。

(3)第三種方式是使用類型註釋。函數

//用於指定類型的類型註釋
var found  /*:Boolean*/ = false; 
var count  /*:int*/  = -1;    
var name   /*:String*/ = "";     
var person /*:Object*/ = null;
  • 缺點在於不能用多行註釋一次註釋大塊的代碼。(在每一行上使用單行註釋可解決這個問題)

1.3 鬆散耦合

只要應用的某個部分過於依賴另外一部分,代碼就是耦合過緊,難以維護。性能

一、解耦HTML/JavaScript
二、解耦CSS/JavaScript測試

  • 因爲JavaScript必須與HTML和CSS共存,因此讓各自徹底定義起本身的目的很是重要:
  • JavaScript應該定義行爲,HTML應該定義內容,CSS應該定義外觀。

三、解耦應用邏輯/事件處理程序大數據

應用和業務邏輯之間鬆散耦合的幾條原則:優化

  • 勿將event對象傳給其餘方法;只傳來自event對象中所需的數據;
  • 任何能夠在應用層面的動做都應該能夠在不執行任何事件處理程序的狀況下進行;
  • 任何事件處理程序都應該處理事件,而後將處理轉交給應用邏輯。

1.4 編程實踐

一、尊重對象全部權this

  • 不要爲實例或原型添加屬性;
  • 不要爲實例或原型添加方法;
  • 不要重定義已存在的方法;

能夠經過如下方式爲對象建立新的功能

  • 建立包含所需功能的新對象,並用它與相關對象進行交互;
  • 建立自定義類型,繼承須要進行修改的類型,而後能夠爲自定義類型添加額外功能;

二、避免全局量

最多建立一個全局變量,讓其餘對象和函數存在其中。

var MyApplication  = {
    name : "Nicholas",
    sayName : function(){
        alert(this.name);
    }
};

命名空間包括建立一個用於放置功能的對象。惟一的全局變量做爲一個容器,其中定義了其餘方法,用這種方式將功能組合在一塊兒的對象,叫作命名空間。命名空間有助於確保代碼能夠在同一頁面上與其餘代碼以無害的方式一塊兒工做。

三、避免與null進行比較

直接將值與null比較是使用過分的,而且經常因爲不充分的類型檢查致使錯誤。若是看到了與null比較的代碼,嘗試使用一下技術替換:

  • 若是值應爲一個引用類型,使用instanceof操做符檢查其構造函數;
  • 若是值應爲一個基本類型,使用typeof檢查其類型;
  • 若是是但願對象包含某個特定的方法名,則使用typeof操做符確保指定名字的方法存在於對象上。

四、使用常量

將數據從應用邏輯分離出來的思想。能夠在不冒引入錯誤的風險的同時,就改變數據。以下例

function validate(value) {
    if(!value) {
        alert("Invalid value");
        location.href = "/errors(invalid.php)";
    }
}

能夠經過將數據抽取出來變成單獨定義的常量的方式,將應用邏輯與數據修改隔離開來。修改以下:

var Constants = {
    INVALID_VALUE_MSG: "Invalid value",
    INVALID_VALUE_URL: "/errors/invalid.php" 
};

function validate(value) {
    if(!value) {
        alert(Constant.INVALID_VALUE_MSG);
        location.href = COnstant.INVALID_VALUE_URL;
    }
}

關鍵在於將數據和使用它的邏輯進行分離。須要提取做爲常量的值大體有如下幾種狀況:

  • 重複值:任何在多處用到的值都應抽取爲一個常量;
  • 用戶界面字符串:任何用於顯示給用戶的字符串,都應該被抽取出來以方便國際化;
  • URLs:在WEB應用中,資源位置很容易變動,因此推薦用一個公共地方存放全部的URL;
  • 任意可能會更改的值

二、性能

2.1 注意做用域

訪問全局變量老是比訪問局部變量慢。只要能減小花費在做用域上的時間,就能增長腳本的總體性能。

一、避免全局查找

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.getElementsById("msg");
    msg.innerHTML = "Update complete."
}

上述函數看起來徹底正常,可是它包含了三個對於全局document對象的引用。經過建立一個指向document對象的局部變量,就能夠經過限制一次全局查找來改進這個函數的性能。改進後的代碼以下:

function updateUI() {
    var doc = document;
    var imgs = doc.getElementsByTagName("img");
    for(var i=0, len=imgs.length; i<len; i++) {
        imgs[i].title = doc.title + "image" + i;
    } 
    var msg = doc.getElementsById("msg");
    msg.innerHTML = "Update complete."
}

二、避免with語句

with語句會建立本身的做用域,所以會增長其中執行的代碼的做用域的長度。
大多數狀況下,能夠是同局部變量完成相同的事情而不引入新的做用域。

function updateBody() {
    with(document.body) {
        alert(tagName);
        innerHTML = "Hello World!";
    }
}

以上代碼中的with語句讓document.body變得更容易使用,可是其實可使用局部變量達到相同的效果。

function updateBody() {
    var body = document.body;
    alert(body.tagName);
    body.innerHTML = "Hello World!";
}

2.2 選擇正確的方法

一、避免沒必要要的屬性查找

  • 一旦屢次用到屬性對象,應該將其存儲在局部變量中。通常來說,只要能減小算法的複雜度,就要儘量減小。儘量多的使用局部變量將屬性查找替換爲值查找。進一步講,若是既能夠用數字化的數組位置進行訪問,也可使用命名屬性,那麼使用數字位置。

二、優化循環

  • (1)減值迭代:不少狀況下,從最大值開始,在循環中不斷減值的迭代器更加高效。
  • (2)簡化終止條件
  • (3)簡化循環體:確保沒有某些能夠被很容易移出循環的密集操做。
  • (4)使用後測循環:最常使用的for循環和while循環都是前測試循環。而如do-while這種後測試循環,能夠避免最終終止條件的計算,所以運行更快。

三、展開循環(能夠考慮一種叫作Duff裝置的技術)
當循環次數是肯定的,消除循環並使用屢次函數調用每每更快。以下循環代碼

for(var i=0; i<values.length; i++) {
    process(values[i]);
}

能夠改成

process(values[0]);
process(values[1]);
process(values[2]);

//Duff裝置的基本概念是經過計算迭代的次數是否爲8的倍數將一個循環展開爲一系列語句。

var iterations = Math.ceil(values.length / 8);
var startAt = values.length % 8;
var i = 0;
do{
    switch(startAt) {
        case 0: process(value[i++]);
        case 7: process(value[i++]);
        case 6: process(value[i++]);
        case 5: process(value[i++]);
        case 4: process(value[i++]);
        case 3: process(value[i++]);
        case 2: process(value[i++]);
        case 1: process(value[i++]);
    }
    startAt = 0;
}while(--iterations > 0);

Math.ceil()函數返回大於或等於一個給定數字的最小整數
Math.floor()函數返回小於或等於一個給定數字的最大整數

DUff裝置的實現是經過將values數組中的元素個數除以8來計算出循環須要進行多少次迭代的。而後使用取整的上限函數確保結果是整數。若是徹底根據8來進行迭代,可能會有一些不能被處理到的元素,這個數量保存在startAt變量中。首次執行該循環時,會檢查startAt變量看有須要多少次額外調用。那麼最開始的時候process()則只會被調用2次。在接下來的循環中,startAt被重置爲0,這樣以後的每次循環都會調用8次process()。展開循環能夠提高大數據及的處理速度。

四、避免雙重解釋

//某些代碼求值——避免!!
eval("alert('Hello World!')");
//建立新函數——避免!!
var sayHi = new Function("alert('Hello World!')");
//設置超時——避免!!
setTimeout("alert('Hello World!')",500);

修改成

//某些代碼求值——已修正
alert('Hello World!');
//建立新函數——已修正
var sayHi =  function(){
    alert('Hello World!')
};
//設置超時——已修正
setTimeout(function(){
    alert('Hello World!');
},500);

五、性能其餘的注意事項

  • 原生方法比較快;
  • Switch語句較快;
  • 位運算符比較快

2.3 最小化語句數

完成多個操做的單個語句要比完成單個操做的多個語句快。

一、多個變量的聲明

//4個語句——浪費!
var count = 5;
var color = "blue";
var values = [1,2,3];
var now = new Date();

修改成

var count = 5;
    color = "blue";
    values = [1,2,3];
    now = new Date();

二、插入迭代值

當使用迭代值(也就是在不一樣的位置進行增長或者減小的值)的時候,儘量使用合併語句。

var name = values[i];
    i++;

例如以上代碼修改成

var name = values[i++];

三、使用數組和對象字面量

建立數組和對象的方法有兩種:使用構造函數或者是字面量。使用構造函數老是要用到更多的語句來插入元素或者定義屬性,而字面量能夠將這些操做在一個語句中完成。

//用4個語句建立和初始化數組——浪費   
var values = new Array();
values[0] = 123;
values[1] = 456;
values[2] = 789;

//只用一條語句建立和初始化數組
var values = [123, 456, 789];

只要有可能,儘可能使用數組和對象的字面量表達方式來消除沒必要要的語句。

2.4 優化DOM交互

一、最小化現場更新

  • 之因此叫現場更新,是由於須要當即(現場)對頁面對用戶的顯示進行更新。
  • 一旦須要更新DOM,請考慮使用文檔片斷來構建DOM結構,而後再將其添加到現存的文檔中。

二、使用innerHTML

  • 對於大的DOM更改,使用innerHTML要比使用標準DOM方法建立一樣的DOM結構快得多。
  • 使用innerHTML的關鍵在於最小化調用它的次數。

三、使用事件代理

  • 任何能夠冒泡的事件都不只僅能夠在事件目標上進行處理,目標的任何祖先節點上也能處理。使用這個只是就能夠將事件處理程序附加到更高層的地方負責多個目標的事件處理。

四、注意HTMLCollection

  • 任什麼時候候要訪問HTMLCollection,無論它是一個屬性仍是方法,都是在文檔上進行一個查詢,這個查詢開銷很昂貴。最小化訪問HTMLCollection的次數能夠極大地改進腳本的性能。當在循環中使用HTMLCollection的時候,下一步應該是獲取要使用的項目的引用,以便避免在循環體內屢次調用HTMLCollection。
var images = document.getElementsByTagName("img");
    i, len;
for(i=0, length=images.length; i<len; i++) {
    //處理   
}

修改成

var images = document.getElementsByTagName("img");
    image,
    i, len;
for(i=0, length=images.length; i<len; i++) {
    image = images[i];
    //處理   
}

編寫JavaScript的時候,必定要知道什麼時候返回HTMLCollection對象,這樣你就能夠最小化他們的訪問。發生一下狀況時會返回HTMLCollection對象:

  • 進行了對getElementByTagName()的調用;
  • 獲取了元素的childNodes屬性;
  • 獲取了元素的attribute屬性;
  • 訪問特殊的集合,如document.forms、document.images等。

當使用HTMLCOllection對象時,合理使用會極大提高代碼的執行速度。

三、部署

一、構建過程
二、驗證
三、壓縮

  • 爲了協助部署,推薦設置一個能夠將JavaScript合併爲較少文件的構建過程;
  • 有了構建過程也能夠對源代碼自動運行額外的處理和過濾,例如能夠運行JavaScript驗證器來確保沒有語法錯誤或者是代碼沒有潛在的問題;
  • 在部署前推薦使用壓縮器將文件儘量變小;
  • 和HTTP壓縮一塊兒使用可讓JavaScript文件儘量小,所以對總體頁面性能的影響也會最小。
相關文章
相關標籤/搜索