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

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

    Javascript的弱類型以及函數做用域等規則使用編寫Javascript代碼極爲容易,可是編寫可維護、高質量的代碼卻變得十分困難,這個系列的文章將總結在項目開發過程當中,可以改善代碼可讀性、可維護性及優化運行性能的一系列技巧。
    若有問題,請不吝指出,很是感謝;若是喜歡,右下角點個推薦吧~java

1.全局變量污染與變量提高

  • 定義全局變量的3種方式
var key = 'value'; // 全部函數外執行
window.key = 'value'; // window爲全局對象
key = 'value'; // 省去 var,隱式的全局變量
  • 全局對象是全部域中均可見的變量,若是程序比較小,那麼定義全局變量能夠避免函數調用時參數的傳遞,可是對於一個大的項目,全局變量定義不善,極爲容易形成全局變量被某個程序改掉,而沒有被發現,這也使調試時難以發現問題所在。
  • 在項目較大時,建議儘量地避免使用全局變量,那麼兼顧靈活性與可讀性的解決方法就是在程序中只建立一個全局變量。
/** 
 * 只有Person一個全局變量,
 * 其他變量定義在Person下,儘量地避免與其餘變量名衝突
 */
var Person = { name:"A", age:20 };
Person.parents = {
    father:"A",
    mother:"C"
}
Person.getAge = function() { /* ... */ }
  • 與C、Python等語言擁有塊級做用域不一樣,Javascript做用域是以函數爲單位的,定義在函數中的變量函數外部不可見,可是在函數內部(包括其子函數)到處可見。
/**
 * 考慮下面的函數,先運行f1(),再var b = 4,
 * 可是事實上,f1()中的b就是以後定義的b
 * 這是變量提高特性形成的,就是說全部變量定義都將提到函數最前面
 * 局部變量的優先級高於外部變量,所以f()外部的b至始至終沒有變過
 */
var b = 15;
var f = function() { 
    var a = 100;
    var f1 = function(){
        var c = 3;
        b = a + c;  // => b=103 變量提高,這裏的b是函數f()內部的b
    };
    f1(); 
    var b = 4;
    console.log(a , b , c); // => a=100,b=4,c=undefined
}
f();  
console.log(b); // => b=15,b並無發生改變

2.數據類型

  • 浮點數是不精確的,在項目中,特別是在線支付環節,若是但願計算的結果是準確的,通常先乘100轉換爲整數後,計算完後再除以100,這樣可以獲得預想中的結果。
0.1 + 0.2 === 0.3 // => false
  • 檢測數據類型,咱們先看面試中常出現的判斷題
typeof null === 'null' // => false
typeof null === 'object' // => true
typeof NaN === 'NaN'  // => false
typeof NaN === 'number' // => true
// javascript6個基本類型以下,typeof老是返回這6個值
// number、string、boolean、object、function、undefined
// chrome 50中,typeof 3/0 結果爲 NaN,有博友知道爲何求告知
  • typeof null 返回的是'object',項目開發中,咱們並不但願null被判斷爲'object',自定義一個函數,能夠知足需求。
function type(ob){
    return (ob === null ) ? "null" : (typeof ob);
}
  • 上面的方法不能識別數組、日期等對象,若是須要判斷日期、數組、正則、錯誤等類型,那麼能夠考慮使用toString()方法。
// 更多內置類型,例如 Math 等能夠根據須要添加
function type(ob){
    var _toString = Object.prototype.toString;
    var _type = {
        "undefined" : "undefined",
        "number" : "number",
        "boolean" : "boolean",
        "string" : "string",
        "[object Function]" : "function",
        "[object RegExp]" : "regexp",
        "[object Array]" : "array",
        "[object Date]" : "date",
        "[object Error]" : "error"
    }
    return _type[typeof ob] || _type[_toString.call(ob)] || (ob ? "object" : "null");
}
  • 自定義類型,可結合typeof、constructor屬性和instanceof實現

3.特殊值(NaN、undefined、null)

typeof NaN === 'number' // => true 類型爲number,表示非數字
'0.1223' // => 0.1223
'1e6' // => 1000000
'1a3' // => NaN
1.2 + NaN // => NaN
  • Javascript提供了isNaN函數檢測NaN值
// 不能轉換爲數字的值,均返回true,
// 所以並不能用這個函數檢測一個值是否真的爲 NaN
isNaN(NaN) // => true
isNaN('abc') // => true
isNaN(0)  // => false
isNaN('1e6') // => false
isNaN(3/0) // => fasle

// NaN是一個惟一本身與本身不相等的值
NaN === NaN // false 
NaN !== NaN // true
  • 如何檢查一個值是否是真的NaN
// 方法一
function _isNaN(value){
    return value !== value;
}
// 方法二
function _isNaN(value) {
    return typeof value === 'number' && isNaN(value);
}
  • 使用isFinite檢查是否能夠是個數或可被轉換爲一個數
isNaN(3/0) // => fasle,雖然3/0 不能表明一個數,可是isNaN不能識別
isFinite(3/0) // => false,isFinite能檢測 NaN和正負無窮大
isFinite("234") // => true,isFinite 能夠將參數轉換爲數字
isFinite(true) // => true,可轉化爲數字的字符串和布爾值返回true
  • 判斷參數是否真的是一個數字,而不是可轉換爲數字的字符串等
// 加一個基本類型判斷就OK
function isNumber (value) {
    return typeof value === "number" && isFinite(value);
}
  • undefined有以下幾種狀況python

    (1) 定義了變量但沒有賦值 var cc; cc === undefined // => true (2) 獲取一個對象不存在的屬性 (3) 沒有返回值的函數,new + 構造函數除外

    (4) 定義函數時聲明多個形參,調用時傳入參數個數少於聲明,多餘的參數爲undefined面試

  • null 只能顯示指定,通常用來清空對象
  • 0、NaN、''(空字符串)、false、null、undefined的布爾值爲false(注意,[](空數組),{}(空對象)布爾值爲 true )chrome

var a = 4 ;
if({})a++; // => a=5 空對象可經過 Object.keys().length判斷
if([])a++; // => a=6 空數組經過 length 判斷

4. === 與 ==

  • 使用 == 比較前若是不是相同類型的值,將進行類型轉換,轉換規則請看下面的例子。
'' == 0  // => true,String與Number,將String轉爲 Number
0 == '0' // => true,同上
'' == '0'// => false,2個都是字符串,所以直接比較
fasle == '0'  // => true,Boolean與其餘,將Boolean轉爲Number
true == '3'  // => false,同上
false == undefined // => false,同上,0與undefined不等
false == 'false'   // => false,同上
null == undefined // => true,null與undefined比較返回true
  • === ,若是類型不一樣,則直接返回false,類型相同再進行比較。
  • == 與 != 將進行隱式的類型轉換,轉換規則複雜,所以在實際的項目開發中,建議避免使用 == 與 !=,儘可能使用 === 與 !==。

5.沒有真正的數組

  • 在Javascript中,數組也是對象,所以typeof的結果是object,若是須要判斷一個對象是否是數組,那麼可使用第2部分的toString()方法,固然還能夠利用構造函數判斷。
typeof value === 'object' && value.constructor === Array
  • 須要注意的是,函數參數arguments並非數組,只是一個具備length屬性,以數字做爲鍵的對象,所以arguments不能使用數組的方法,可是利用上述方法仍將arguments判斷爲數組。
  • 有時咱們須要對arguments對象執行數組的某些方法,這個時候能夠利用下面的方法將arguments轉換爲真數組
/* 將arguments對象轉換爲數組 */
var args = Array.prototype.slice.call(arguments);
/* 具備length屬性,鍵爲數字且從0開始的對象 */
a = {0:"c",1:"d",length:2 }
Array.prototype.slice.call(a); // => ["c","d"]
/* 沒有length屬性 */
a = {0:"c",1:"d"}
Array.prototype.slice.call(a); // => [ ] 空數組
/* 猜想slice方法實現方式 */:
function slice(ob){
    var arr = new Array(); 
    for(var i = 0; i < ob.length; i++ ){
        arr[i] == ob[i];
    }
    return arr;
}

6.避免使用with與eval

var ob = { a: 1, b: 2};
var d = 0;
with(ob){
    a = 3;  // ob.a = 3
    c = 4;  // ob.c = undefined,window.c = 4,無意聲明瞭全局變量
    d = 5;  // ob.d = undefined, d爲with外申明的變量
}
  • 不推薦使用with的3個理由數組

    (1) 使用with時,先檢查對象是否有該屬性,存在則使用,不存在則繼續查找做用域,會下降執行性能
    (2) with語句中的變量語意不明確,可讀性差

    (3) 例如上例中的c不當心隱式聲明爲全局變量,本意是給ob.c賦值,易出現問題且難以調試安全

  • 對於eval(),能夠直接執行一段js代碼,不推薦理由以下函數

    (1) eval須要將字符串解釋爲代碼後執行,下降執行性能
    (2) eval下降程序的可讀性,例如 eval("var a = ob." + key)等價於var a=ob[key],後者明顯更優

    (3) eval存在安全問題,任意傳入字符串均可能被解析執行,惡意代碼也不例外性能

7.消除switch歧義

  • switch中的case語句,定義了函數的起點,卻沒有定義函數的終點,不要忘記case語句後使用break
  • 有時候,咱們確實須要貫穿case語句,好比多種case狀況,執行同一個方法,這時候,建議顯式聲明,消除歧義
// 在須要貫穿的case語句後添加 /* empty */
// 避免檢查代碼時被認爲忘記寫了執行語句
// 顯示申明,語意也更明確,提升可讀性
switch(a){
    case 1: /* empty */
    case 2:
        func1();
        break;
    case 3:
        func2();
        break;
}

8.不要省略塊標誌 { }

  • if、while、do、for等語句接受{ ... } 代碼塊,可是有時咱們在代碼塊中只有一行代碼,爲了使代碼看起來簡潔,極可能省略 { },可是這可能產生問題。
if(a)
    if(b)
        func1();
else
    func2();
// => 等價於
if(a)
    if(b)
        func1();
    else
        func2();
// 這並非想要的結果,js不是python,仍是加上大括號比較好
  • 加上括號,也加強了代碼的可讀性,下面這種寫法是否是一目瞭然了呢
// 很清晰地看出 if(a) 和 else 並列
// if(b) 是 if(a) 的子語句
if(a){
    if(b){
        func1();
    }
}else{
    func2();
}

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

相關文章
相關標籤/搜索