由於代碼維護的成本是巨大的,你思如泉涌的時候,幾個小時洋洋灑灑寫出來的代碼,在未來發生bug或者發生新需求或者須要移植的時候,可能須要一週的時間來閱讀,再花費一週的時間來修改。
書寫可維護的代碼,須要作到如下幾點:javascript
全局變量是魔鬼。
JavaScript經過函數來管理做用域,在函數內部生命的變量只能夠在函數內部使用,反之,就是全局變量。
每一個JavaScript環境都有一個全局對象,能夠經過this來訪問這個全局對象,建立的每個全局變量,都是這個全局對象的一個屬性,在瀏覽器中,爲了方便起見,這個全局對象會有一個附加屬性--window,一般this=window。java
global = "全局變量" // 建立一個全局變量 console.log(global); console.log(window.global); console.log(window["global"]); console.log(this.global); // 輸出的所有都是「全局變量」
儘管可使用命名空間模式或者當即執行函數來避免產生全局變量,可是最好的辦法仍是使用var來定義變量。
可是因爲JavaScript的特性,很容易就不自覺的建立出全局變量了。正則表達式
所以,在如下例子中,很容易就會產生全局變量而不自覺。api
var getMax = function(x,y){ if( x > y ){ ret = x ; } else { ret = y ; } return ret; }; getMax(3,4); alert(ret); // 在這個例子中,由於ret沒有被聲明,所以,他變成了一個全局變量。 // 最終執行,會alert 4
另外一種產生全局變量的狀況以下:數組
var getMax = function(){ var a = b = 0; }; getMax(); alert(b); // 以上代碼中a是局部變量,而b則是全局變量 // 由於在從右向左的賦值過程當中,以上代碼實際等於var a = (b=0) // 所以b變成了全局變量
在函數頂部,使用單var語句是比較好用的方法,例如:瀏覽器
var foo = function(){ var a = 0, b = 1, c = true, d = "hello", e = a + b, arr = [], obj = {}; }; //同時,也能夠在單var語句的聲明中作一些實際的事情例如: var modifyDom = function(){ var el = document.getElementById("id"), style = el.style;// 例如這裏,取得element的樣式 };
var global = 1 ; // 顯示定義 global2 = 2 ; // 隱式 function foo () { global3 = 3;// 隱式 } delete global ; // false 顯示定義的變量不能刪除 delete global2; // true 隱式定義的能夠刪除 delete global3; // true 隱式定義的能夠刪除 alert (typeof global); // number alert (typeof global2); // undefined alert (typeof global3); // undefined
JavaScript中,能夠在函數的任何地方進行聲明,可是他們好像都在函數的頂部被聲明過了(自上而下的運行方式中,即便聲明在下方,使用在上方也不會有問題),這個現象稱之爲hoisting(懸置,置頂解析,預解析)
所以若是不注意這個問題,不在函數頂部單var模式預先聲明的話就會遇到一些問題:緩存
temp = "hello"; function foo(){ alert(temp); var temp = "nihao"; alert(temp); } foo(); // 這段代碼第一反應時彈出hello 和 nihao // 可是結果倒是undefined 和 nihao // 這是由於這段代碼實際上等於 temp = "hello"; function foo(){ var temp ; alert(temp); var temp = "nihao"; alert(temp); } foo(); // 在函數頂部先執行了 var temp; // 這樣全局變量就被沖掉了
// 看一下運行結果 temp = "hello"; alert(temp); // hello function foo(){ alert(temp); // undefined var temp = "nihao"; alert(temp); // nihao } foo(); // 該例子說明在函數內部,頂部的預編譯
在for循環中,你能夠循環取得數組或是數組相似對象的值,譬如arguments和HTMLCollection對象。一般的循環形式以下:函數
for (var i = 0; i < myarray.length; i++) { // 使用myarray[i]作點什麼 }
這種形式的循環的問題在於,每次循環的時候,數組的長度都要從新去獲取一次,這會下降效率,尤爲是當循環的對象不是一個數組而是一個HTML Collections的時候,這也就是爲何採用如下方法進行循環會比較好的緣由oop
for(var i= 0, max = myarray.length; i< max; i++){ // 使用myarray[i]作點什麼 }
這樣,在整個循環中,只檢索了一次數組的長度。this
伴隨着單var形式,以上代碼能夠進行如下修改
function loop(){ var i, max, myarray= []; for (i=0,max=myarray.length;i<max;i++){ //使用myarray[i]作點什麼 } }
另外JSLint建議使用i=i+1或者i+=1來代替i++
由於這樣會使代碼看起來過於複雜,這點我的認爲能夠無視
以上代碼還有兩種變化:
var max,myarray=[]; for(max=myarray.length;max--){ //使用myarray[max]來作點什麼 } var myarray=[], max = myarray.length; while(max--){ //使用myarray[i]作點什麼 }
for-in循環應該運用在非數組對象的遍歷上,使用for-in進行循環也被成爲「枚舉「。
雖然在技術上可使用for-in去循環遍歷數組,可是並不推薦這樣作。
有個很重要的方法:hasOwnProperty,當遍歷對象屬性時,能夠過濾掉原型鏈上繼承來的屬性。
以下所示:
var man ={ heads:1, leg:2, hands:2 };// 定義一個名爲man的對象字面量 if(typeof Object.prototype.clone == "undefined"){ Object.prototype.clone = function(){ //clone }; }// 在對象原型上增長clone方法 for (var i in man) { if (man.hasOwnProperty(i)) { // 過濾 console.log(i, ":", man[i]); } }
若是不使用hasOwnProperty方法來過濾,那麼結果將會是
heads : 1 leg : 2 hands : 2 clone : (){ //clone }
另外一種實用hasOwnProperty的方法以下:
for(var i in man){ if (Object.prototype.hasOwnProperty.call(man, i)) { // 過濾 console.log(i, ":", man[i]); } }
這裏還可使用單var模式,把Object.prototype.hasOwnProperty"緩存"起來
var i,has = Object.prototype.hasOwnProperty; for(i in man){ if(has.call(man,i)){ //過濾 console.log(i,":",man[i]); } }
能夠經過如下形式寫法加強代碼健壯性
var inspect_me = 0, result = ''; switch (inspect_me) { case 0: result = "zero"; break; case 1: result = "one"; break; default: result = "unknown"; }
這個簡單的例子中所遵循的風格約定以下:
JavaScript的變量在比較的時候會隱式類型轉換。這就是爲何一些諸如:false = = 0 或 「」 = = 0 返回的結果是true。爲避免引發混亂的隱含類型轉換,在你比較值和表達式類型的時候始終使用= = =和!==操做符。
好比:
var zero = 0; if (zero === false) { // 不執行,由於zero爲0, 而不是false } // 反面示例 if (zero == false) { // 執行了... }
以大寫字母寫構造函數(Capitalizing Constructors)
JavaScript並無類,但有new調用的構造函數:
var adam = new Person(); //由於構造函數仍僅僅是函數,僅看函數名就能夠幫助告訴你這應該是一個構造函數仍是一個正常的函數。 //命名構造函數時首字母大寫具備暗示做用,使用小寫命名的函數和方法不該該使用new調用: function MyConstructor() {...} function myFunction() {...}
當你的變量或是函數名有多個單詞的時候,最好單詞的分離遵循統一的規範,有一個常見的作法被稱做「駝峯(Camel)命名法」,就是單詞小寫,每一個單詞的首字母大寫。
對於構造函數,可使用大駝峯式命名法(upper camel case),如MyConstructor()。對於函數和方法名稱,你可使用小駝峯式命名法(lower camel case),像是myFunction(), calculateArea()和getFirstName()。
要是變量不是函數呢?開發者一般使用小駝峯式命名法,但還有另一種作法就是全部單詞小寫如下劃線鏈接:例如,first_name, favorite_bands, 和 old_company_name,這種標記法幫你直觀地區分函數和其餘標識——原型和對象。
ECMAScript的屬性和方法均使用Camel標記法,儘管多字的屬性名稱是罕見的(正則表達式對象的lastIndex和ignoreCase屬性)。
JavaScript中沒有定義常量的方法(儘管有些內置的像Number, MAX_VALUE),因此開發者都採用所有單詞大寫的規範來命名這個程序生命週期中都不會改變的變量,如:
// 珍貴常數,只可遠觀 var PI = 3.14, MAX_WIDTH = 800;
還有另一個徹底大寫的慣例:全局變量名字所有大寫。所有大寫命名全局變量能夠增強減少全局變量數量的實踐,同時讓它們易於區分。
另一種使用規範來模擬功能的是私有成員。雖然能夠在JavaScript中實現真正的私有,可是開發者發現僅僅使用一個下劃線前綴來表示一個私有屬性或方法會更容易些。考慮下面的例子:
var person = { getName: function () { return this._getFirst() + ' ' + this._getLast(); }, _getFirst: function () { // ... }, _getLast: function () { // ... } }; 在此例中,getName()就表示公共方法,部分穩定的API。而_getFirst()和_getLast()則代表了私有。它們仍然是正常的公共方法,可是使用下劃線前綴來警告person對象的使用者這些方法在下一個版本中時不能保證工做的,是不能直接使用的。注意,JSLint有些不鳥下劃線前綴,除非你設置了noman選項爲:false。 下面是一些常見的_private規範: 使用尾下劃線表示私有,如name_和getElements_() 使用一個下劃線前綴表_protected(保護)屬性,兩個下劃線前綴表示__private (私有)屬性 Firefox中一些內置的變量屬性不屬於該語言的技術部分,使用兩個前下劃線和兩個後下劃線表示,如:__proto__和__parent__。