JavaScript需要避免的問題總結

JavaScript需要避免的問題總結

最近看了Douglas Crockford的《JavaScript 語言精粹》,對於JavaScript有了新的瞭解,本文主要總結一下JavaScript語言特性引起的一些常見問題,以及避免方法。


1.檢索

  • JavaScript檢索對象包含值的方法有兩種,一種是”[]”,另一種是”.”,一般兩種方式都可用。但在檢索的字符串是一個非法JavaScript標示符或者爲保留字時,”.”是不可用的,只能使用name[“first-name”]
    (在對象字面量定義時也是如此,由於“first-name”是非法標示符,所以定義時必須用引號包含——“first-name:”“Joe”)。
    書中建議使用”.”方法檢索,也就是name.first_name。所以就需要編碼時注意標示符的合法性,以及不要和保留字衝突

  • 另外一個更重要的問題是,檢索一個不存在的成員屬性時,將返回undefined,而從undefined中取值會導致TypeError異常,例:age不爲name對象的屬性,name.age爲undefined,name.age.child則會拋出異常。解決方法爲 設置默認值var age = name.age || “unknown”, 也可以通過“&&”運算避免錯誤: name.age&&name.age.child

2.枚舉

for in 語句可以遍歷一個對象中所有屬性名,但同時還包括函數和原型中的屬性。解決方法一就是,過濾掉不需要的屬性

var name;
for(name in names){
  if(names.hasOwnProperty(name) && typeof names[name] !=='function'){
    document.writeln(name + ' : ' + names[name]);
  }
}

其中hasOwnProperty方法檢測對象是否有指定屬性,且不檢查原型鏈;若屬性值爲函數,typeof返回’function’類型。
另一種解決方法就是,完全避免使用for in語句,使用常規for語句
因爲for in語句除了上述問題以外,枚舉的屬性名出現的順序是不確定的,若要使順序確定,則可以創建一個數組,以正確的順序包含屬性名,然後用for語句遍歷

var i;
var properties =[
    'first_name',
    'middle_name',
    'last_name'
    ];
for(i=0; i<properties.length; i+=1){
    document.writeln(properties[i] + ':' + names[properties[i]]);
}

這樣既保證了順序,又直接確定了需要的屬性。(但這種方法需要自己創建數組,且需要自己確定屬性名,雖然書中推薦這種方法,但還要看具體情況)

3.減少全局污染

Douglas Crockford在書中指出,JavaScript對全局變量的依賴是所有糟糕特性中最糟糕的一個,因爲全局變量可以被程序的任何部分在任意時間修改,降低了程序的可靠性,且全局變量名稱會和子程序變量名稱產生衝突,導致程序無法運行且難以調試。
共有3種方式定義全局變量:

  1. 在任何函數之外聲明: var foo = value;
  2. 直接給全局對象添加屬性: window.foo = value; (window爲web瀏覽器的全局對象)
  3. 直接使用未經聲明的變量: foo = value; (即隱式的全局變量)

減少全局污染的方法一:最小化使用全局變量 ,一個應用只創建一個唯一全局變量

var MYAPP = {};
MYAPP.name = {
    ...
};
MYAPP.method={
    ...
};

另一種減少全局污染的方法是,使用函數和“閉包”構建模塊來進行信息隱藏
“內部函數可以訪問它們外部函數的參數和變量(除了this和arguments),內部函數擁有比它的外部函數更長的生命週期,內部函數訪問外部函數的實際變量而無需複製。”

  • 所謂“閉包”即是函數可以訪問它被創建時所處的上下文環境(可以簡單理解爲內部函數可以訪問外部函數定義的變量)。
  • 模塊即利用函數作用域和“閉包”構建的只提供接口,隱藏其中狀態信息的函數或對象。

這裏舉書中的一個例子:

var serial_maker = function(){
    var prefix = '';
    var seq = 0;
    return {
        set_prefix: function(p){
            prefix=String(p);
        },
        set_seq: function(s){
            seq = s;
        },
        gensym: function(){
            var result = prefix + seq;
            seq +=1;
            return result;
        }
    };
};

var seqer = serial_maker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym();

seqer只提供接口方法set_prefix、set_seq和gensym,由於函數作用域,私有變量對其他程序是不可見的,只能通過這些方法改變prefix 和seq的值。

也可以構造一個對象:

var myObj = (function() {
    var value=0;
    return {
        increment: function(inc){
            value += typeof inc === 'number'?inc:1;
        },
        getValue: function(){
            return value;
        }
    };
}());
myObj.increment(1);
myObj.getValue();

注意“()”,以上返回的是函數運行的結果,即包含兩個方法的對象。

模塊模式的一般形式爲第一種方式:定義了私有變量和函數的函數;利用閉包創建訪問私有變量和函數的接口函數;最後返回這個函數,或者放到可以訪問的地方。

利用模塊模式可以摒棄全局變量的使用。
可參考另外一篇文章JavaScript的幾點編碼規範提高代碼質量

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