Javascript 優化項目代碼技巧之語言基礎(二)

博客逐步遷移至 極客兔兔的小站javascript

    上一篇隨筆介紹瞭如何正確判斷對象類型、避免變量污染,特殊值(null、undefined、NaN)的使用,以及其餘Javascript中經常使用關鍵字與方法的優化,這篇隨筆將着重介紹Javascript語言中的條件與循環優化。
    若有問題,請不吝指出,很是感謝;若是喜歡,右下角點個推薦吧~html

1.if、switch、查表

1.1 if-else分治策略

// 方法一,假設value的值平均分佈
// 方法一的平均查詢次數是 (n+1)/2,即複雜度是O(N)
// 方法二採用了二分查找,平均查找次數爲lg(n),複雜度是對數級別的
if(value === 0){
    func0();
} else if (value === 1){
    func1();
} else if (value === 2){
    func2();
} else if (value === 3){
    func3();
} else if (value === 4){
    func4();
} else if (value === 5){
    func5();
} else if (value === 6){
    func6();
} else if (value === 7){
    func7();
}
// 方法二(分治策略),爲方便排版,壓縮、省略部分代碼塊
if (value < 4) {
    if(value < 2){
        if(value === 0){ func0(); }
        else { func1(); }
    }else {
        if(value === 2){ func2(); }
        else { func3(); }
    }
} else {
    // 省略同上的結構...
}
  • 優化技巧一:二分查找可以顯著下降複雜度,提升性能,上述例子只是其中一個,在項目中涉及到判斷,查找而數據量較大時能夠考慮二分提升性能。
  • 優化技巧二:對於if-else語句,判斷狀況較少,或者考慮代碼可讀性(好比判斷週一到週五),方法一會更直觀,不能一味追求效率。
  • 優化技巧三:若是判斷值不是平均分佈的,那麼考慮將出現頻率高的判斷放在前面(上例中若是90%狀況下value等於7,幾乎每次都要判斷8次,若是將這個判斷放在最前面,就降到了1次)。

1.2 什麼狀況下用switch

  • switch-case語句利用轉發表,快速定位入口,在數據量較大時,switch-case執行效率會比if-else好不少。
  • 數據量較多時,switch-case的可讀性也會好不少。
  • 如下狀況適合用switchjava

    (1) 判斷值超過2個,並且值是離散的。 例如 case '男' (2) 表達式的值是固定的,不是動態變化的

    (3) 表達式的值值通常爲整數、字符串等類型的數據node

1.3 使用查表提升性能

  • 當有大量離散值須要測試時,若是有一張轉發表能夠直接查詢,那麼無疑能夠極大地提升性能,若是將這個表以字典(dict)的形式呈現,可讀性也獲得了提升。Javascript中字典每每等價於對象,舉兩個例子:
/* 適用狀況一:條件執行代碼不多,如使用if或switch顯得笨重 */
function func1(key){
    if(key === 0){ return ans0; }
    if(key === 1){ return ans1; }
    // 省略 2,3,4,5...
}
// 替換爲
function func1(key){
    var ans = [ans0,ans1,ans2,ans3 ... ];
    return ans[key];
}
/* 適用狀況二:條件執行代碼不少,如用if或switch可讀性不好 
 * 同時,執行代碼可能屢次複用,並且總體性較強
 * 這種寫法經常使用在javasript框架中,書寫鉤子函數(Hook)
 * 可讀性很高,並且容易擴展,代碼自己即文檔
 */
function func1(key){
    if(key === "created"){  /* ... 省略10行代碼 */ }
    else if(key === "init"){ /* ... 省略10行代碼 */ }
    // 省略 "resume","start","pause","destroy",...
}
// 替換爲
var hooks = {
    "created": function(){ /* ... 省略10行代碼 */ };
    "init": function(){ /* ... 省略10行代碼 */ };
    // ... 省略更多屬性
}
function func1(key){ 
    hooks[key]();
}

2.短路表達式(&&和||)

2.1 改善 && 與 || 性能

  • 使用&&運算時,從左到右計算表達式,一旦爲false就返回,例如:數組

    A && B,若是A爲false,表達式返回false,再也不計算B。

    A爲true,會繼續計算B,再決定返回值,這稱爲短路表達式
    所以,若是B爲false的機率大於A,寫爲B && A能夠減小計算次數緩存

  • 使用||運算時,一樣先計算||左側的表達式,例如:框架

    A || B,若是A爲true,表達式返回true,再也不計算B。

    所以,若是B爲true的機率大於A,寫爲B || A能夠減小計算次數函數

2.2 巧用短路表達式賦值

  • &&|| 不只用在判斷語句中,經常使用在賦值語句中
a = {} || [] // => a = {},a並無被賦值爲true,而是 {}
  • 利用javascript的弱類型,咱們能夠構造很精巧的賦值表達式,0、""、null、false、undefined、NaN 判斷爲false,其餘爲true
/* || */
var value = a || [];
// 等價於
value = a ? a : [];
// 等價於
if(a){ value = a; }
else { value = []; } 
 
/* && */
var value = a && b;
// 等價於
value = a ? b : a;
  • 當表達式較簡單時,使用三目運算符也能讓咱們的代碼很優雅,可是當表達式數量不少時,&&和||能更優雅。
/* 當a和b均返回false時,返回一個新建對象 */
return a || b || {};

/* jQuery中大量使用短路表達式賦值 
 * 代碼過於簡潔,代碼可讀性會下降,適當使用
 */
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
        (~b.sourceIndex || MAX_NEGATIVE) -
        (~a.sourceIndex || MAX_NEGATIVE);
  • Javascript的弱類型使得短路表達式賦值很容易,可是有時咱們須要明確返回一個布爾值,這個時候能夠強制類型轉換。
var value = !!( a || b) // => value = true/false 
if(!!(a && b)) // => 顯式轉換,與if(a && b)結果一致

3.循環優化

3.1 少用 forEach

  • Javascript的數組有一個原型方法:forEach,能夠在每個成員上執行一個函數,forEach接收3個參數,數組項的值(value),數組項的索引(index),以及數組自身(array)
  • forEach 相比通常的循環,將關聯額外的函數調用,若是將forEach改成普通的循環結構,效率將大大提升。
items = [1,2,3];
items.forEach(function(value,index,array) {
    // ... func(value);
})
// 等價於
var len = items.length;
for(var i = 0; i < len; ++i ){
    // ... func(items[i]);
}

3.2 重複變量暫存

  • 在循環中,重複使用一個變量的狀況是十分常見的,例如上個例子的中len,若是不使用變量len暫存,那麼每次循環都須要訪問數組的length屬性,比起直接訪問一個數字,訪問一個對象屬性的花銷會更大。
  • 變量暫存在這個例子中優點並無那麼明顯,可是若是循環中進行了DOM操做,咱們知道DOM操做是至關費時的,若是將DOM對象緩存,性能提升就很可觀了。
// 暫存變量前,獲取 p#item,須要3次DOM操做,
$('p#item').func1();  
$('p#item').func2();
$('p#item').func3();
// 等價於(這裏不考慮jQuery的鏈式操做)
// 只須要一次DOM操做(DOM操做很是耗時)
var p_item = $('p#item');
p_item.fun1();
// ...
  • 重複使用的須要計算的變量賦值給另外一個變量,還有一個好處是易於壓縮,考慮下面的例子
/* 不暫存變量 */
var obj = { nickname:{} };
obj['nickname'].firstname = 'Tom';
obj['nickname'].lastname = 'Smith';
obj['nickname'].fullname = 'Tom Smith';
// 壓縮結果
var a = { nickname:{} };
a['nickname'].firstname = 'Tom';
a['nickname'].lastname = 'Smith';
a['nickname'].fullname = 'Tom Smith';

/* 暫存變量 */
var obj = { nickname:{} };
var nickname = obj['nickname'];
nickname.firstname = 'Tom';
nickname.lastname = 'Smith';
nickname.fullname = 'Tom Smith';
// 壓縮結果以下
var a = { nickname:{} };
var b = a['nickname'];
b.firstname = 'Tom';
b.lastname = 'Smith';
b.fullname = 'Tom Smith';
  • 實際項目中,Javasript文件的代碼動輒上萬行,變量暫存以後,對性能影響以及壓縮後節省的空間不可小視,定義變量時,不妨慷慨一點。

分享創造價值,喜歡這個系列文章,不妨關注一下~性能

相關文章
相關標籤/搜索