1.為什要遵守代碼規(guī)范
軟件bug的修復是昂貴的害驹,并且隨著時間的推移捍掺,這些bug的成本也會增加,尤其當這些bug潛伏并慢慢出現在已經發(fā)布的軟件中時。當你發(fā)現bug 的時候就立即修復它是最好的课兄,此時你代碼要解決的問題在你腦中還是很清晰的。否則晨继,你轉移到其他任務烟阐,忘了那個特定的代碼,一段時間后再去查看這些代碼就 需要:
- 花時間學習和理解這個問題
- 花時間是了解應該解決的問題代碼
- 還有問題紊扬,特別對于大的項目或是公司蜒茄,修復bug的這位伙計不是寫代碼的那個人(且發(fā)現bug和修復bug的不是同一個人)。因此餐屎,必須降低理解代 碼花費的時間檀葛,無論是一段時間前你自己寫的代碼還是團隊中的其他成員寫的代碼。這關系到底線(營業(yè)收入)和開發(fā)人員的幸福腹缩,因為我們更應該去開發(fā)新的激動 人心的事物而不是花幾小時幾天的時間去維護遺留代碼屿聋。
另一個相關軟件開發(fā)生命的事實是,讀代碼花費的時間要比寫來得多庆聘。有時候胜臊,當你專注并深入思考某個問題的時候,你可以坐下來伙判,一個下午寫大量的代碼象对。
你的代碼很能很快就工作了,但是宴抚,隨著應用的成熟勒魔,還會有很多其他的事情發(fā)生,這就要求你的進行進行審查菇曲,修改冠绢,和調整。例如:
- bug是暴露的
- 新功能被添加到應用程序
- 程序在新的環(huán)境下工作(例如常潮,市場上出現新想瀏覽器)
- 代碼改變用途
- 代碼得完全從頭重新弟胀,或移植到另一個架構上或者甚至使用另一種語言
由于這些變化,很少人力數小時寫的代碼最終演變成花數周來閱讀這些代碼喊式。這就是為什么創(chuàng)建可維護的代碼對應用程序的成功至關重要孵户。
可維護的代碼意味著:
- 可讀的
- 一致的
- 可預測的
- 看上去就像是同一個人寫的
- 已記錄
2.編寫代碼需遵守的幾個原則
提示: 不遵守這些原則代碼也能運行起來。只是可能出現難以維護的現象岔留。規(guī)范就像一種模式夏哭,大家按照一種模式來,那么閱讀其他人的代碼献联,成本就降低了竖配。
編寫代碼注意事項:
2.1. 盡量減少聲明全局變量
2.2. 定義變量是何址,盡量放到頂部
function func() {
var a = 1,
b = 2,
sum = a + b,
myobject = {},
i,
j;
// function body...
}
注意:在es6中,使用let 定義进胯,可能出現'暫時性死區(qū)', 具體想知道什么叫做'暫時性死區(qū)' , 請查看阮一峰 ECMAScript 6 入門
2.3.for循環(huán)(for Loops)
// 次佳的循環(huán)
for (var i = 0; i < myarray.length; i++) {
// 使用myarray[i]做點什么
}
//更好的方式
for (var i = 0, max = myarray.length; i < max; i++) {
// 使用myarray[i]做點什么
}
JSLint提示您這樣做用爪,原因是++和–-促進了“過分棘手(excessive trickiness)”。//zxx:這里比較難翻譯龄减,我想本意應該是讓代碼變得更加的棘手
如果你直接無視它项钮,JSLint的plusplus選項會是false(默認是default)班眯。
還有兩種變化的形式希停,其又有了些微改進,因為:
少了一個變量(無max)
-
向下數到0署隘,通常更快宠能,因為和0做比較要比和數組長度或是其他不是0的東西作比較更有效率
//第一種變化的形式: var i, myarray = []; for (i = myarray.length; i–-;) { // 使用myarray[i]做點什么 } //第二種使用while循環(huán): var myarray = [], i = myarray.length; while (i–-) { // 使用myarray[i]做點什么 }
面兩種情況優(yōu)于前面兩種情況。
2.4.for-in循環(huán)(for-in Loops)
for-in循環(huán)應該用在非數組對象的遍歷上磁餐,使用for-in進行循環(huán)也被稱為“枚舉”违崇。
從技術上將,你可以使用for-in循環(huán)數組(因為JavaScript中數組也是對象)诊霹,但這是不推薦的羞延。因為如果數組對象已被自定義的功能增強,就可能發(fā)生邏輯錯誤脾还。另外伴箩,在for-in中,屬性列表的順序(序列)是不能保證的鄙漏。所以最好數組使用正常的for循環(huán)嗤谚,對象使用for-in循環(huán)。
有個很重要的hasOwnProperty()方法怔蚌,當遍歷對象屬性的時候可以過濾掉從原型鏈上下來的屬性
// 對象
var man = {
hands: 2,
legs: 2,
heads: 1
};
// 在代碼的某個地方
// 一個方法添加給了所有對象
if (typeof Object.prototype.clone === "undefined") {
Object.prototype.clone = function () {};
}
====================================================================
// for-in 循環(huán)
for (var i in man) {
if (man.hasOwnProperty(i)) { // 過濾
console.log(i, ":", man[i]);
}
}
/* 控制臺顯示結果
hands : 2
legs : 2
heads : 1
*/
==========================================================================
// 反面例子:
// for-in loop without checking hasOwnProperty()
for (var i in man) {
console.log(i, ":", man[i]);
}
/*
控制臺顯示結果
hands : 2
legs : 2
heads : 1
clone: function()
*/
2.5.(不)擴展內置原型((Not) Augmenting Built-in Prototypes)
增加內置的構造函數原型(如Object(), Array(), 或Function())挺誘人的巩步,但是這嚴重降低了可維護性,因為它讓你的代碼變得難以預測桦踊。使用你代碼的其他開發(fā)人員很可能更期望使用內置的 JavaScript方法來持續(xù)不斷地工作椅野,而不是你另加的方法。
因此籍胯,不增加內置原型是最好的竟闪。你可以指定一個規(guī)則,僅當下面的條件均滿足時例外:
可以預期將來的ECMAScript版本或是JavaScript實現將一直將此功能當作內置方法來實現芒炼。例如瘫怜,- 你可以添加ECMAScript 5中描述的方法,一直到各個瀏覽器都迎頭趕上本刽。這種情況下鲸湃,你只是提前定義了有用的方法赠涮。
如果您檢查您的自定義屬性或方法已不存在——也許已經在代碼的其他地方實現或已經是你支持的瀏覽器JavaScript引擎部分。
-
你清楚地文檔記錄并和團隊交流了變化暗挑。
if (typeof Object.protoype.myMethod !== "function") { Object.protoype.myMethod = function () { // 實現... }; }
一般情況下笋除,強烈不建議使用
2.6.避免隱式類型轉換(Avoiding Implied Typecasting )
JavaScript的變量在比較的時候會隱式類型轉換。這就是為什么一些諸如:false == 0 或 “” == 0 返回的結果是true炸裆。為避免引起混亂的隱含類型轉換垃它,在你比較值和表達式類型的時候始終使用===和!==操作符。
var zero = 0;
if (zero === false) {
// 不執(zhí)行烹看,因為zero為0, 而不是false
}
// 反面示例
if (zero == false) {
// 執(zhí)行了...
}
2.7. 避免(Avoiding) eval()
如果你現在的代碼中使用了eval()国拇,記住該咒語“eval()是魔鬼”。此方法接受任意的字符串惯殊,并當作JavaScript代碼來處理酱吝。當有 問題的代碼是事先知道的(不是運行時確定的),沒有理由使用eval()土思。如果代碼是在運行時動態(tài)生成务热,有一個更好的方式不使用eval而達到同樣的目 標。例如己儒,用方括號表示法來訪問動態(tài)屬性會更好更簡單:
// 反面示例
var property = "name";
alert(eval("obj." + property));
// 更好的
var property = "name";
alert(obj[property]);
3.編碼規(guī)范
3.1 縮進(Indentation)
代碼沒有縮進基本上就不能讀了崎岂。唯一糟糕的事情就是不一致的縮進,因為它看上去像是遵循了規(guī)范闪湾,但是可能一路上伴隨著混亂和驚奇冲甘。重要的是規(guī)范地使用縮進。
一些開發(fā)人員更喜歡用tab制表符縮進响谓,因為任何人都可以調整他們的編輯器以自己喜歡的空格數來顯示Tab损合。有些人喜歡空格——通常四個,這都無所謂娘纷,只要團隊每個人都遵循同一個規(guī)范就好了嫁审。這本書,例如赖晶,使用四個空格縮進律适,這也是JSLint中默認的縮進。
什么應該縮進呢遏插?規(guī)則很簡單——花括號里面的東西捂贿。這就意味著函數體,循環(huán) (do, while, for, for-in)胳嘲,if厂僧,switch,以及對象字面量中的對象屬性了牛。下面的代碼就是使用縮進的示例:
function outer(a, b) {
var c = 1,
d = 2,
inner;
if (a > b) {
inner = function () {
return {
r: c - d
};
};
} else {
inner = function () {
return {
r: c + d
};
};
}
return inner;
}
3.2 花括號{}(Curly Braces)
// 糟糕的實例
for (var i = 0; i < 10; i += 1)
alert(i);
// 好的實例
for (var i = 0; i < 10; i += 1) {
alert(i);
}
3.3 左花括號的位置(Opening Brace Location)
這個實例中颜屠,仁者見仁智者見智辰妙,但也有個案,括號位置不同會有不同的行為表現甫窟。這是因為分號插入機制(semicolon insertion mechanism)——JavaScript是不挑剔的密浑,當你選擇不使用分號結束一行代碼時JavaScript會自己幫你補上。這種行為可能會導致麻 煩粗井,如當你返回對象字面量尔破,而左括號卻在下一行的時候
// 警告: 意外的返回值
function func() {
return
// 下面代碼不執(zhí)行
{
name : "Batman"
}
}
// 警告: 意外的返回值
function func() {
return undefined;
// 下面代碼不執(zhí)行
{
name : "Batman"
}
}
3.4 空格(White Space)
空格的使用同樣有助于改善代碼的可讀性和一致性。在寫英文句子的時候浇衬,在逗號和句號后面會使用間隔懒构。在JavaScript中,你可以按照同樣的邏輯在列表模樣表達式(相當于逗號)和結束語句(相對于完成了“想法”)后面添加間隔径玖。
適合使用空格的地方包括:
- for循環(huán)分號分開后的的部分:如for (var i = 0; i < 10; i += 1) {...}
- for循環(huán)中初始化的多變量(i和max):for (var i = 0, max = 10; i < max; i += 1) {...}
- 分隔數組項的逗號的后面:var a = [1, 2, 3];
- 對象屬性逗號的后面以及分隔屬性名和屬性值的冒號的后面:var o = {a: 1, b: 2};
- 限定函數參數:myFunc(a, b, c)
- 函數聲明的花括號的前面:function myFunc() {}
- 匿名函數表達式function的后面:var myFunc = function () {};
使用空格分開所有的操作符和操作對象是另一個不錯的使用痴脾,這意味著在+, -, *, =, <, >, <=, >=, ===, !==, &&, ||, +=等前后都需要空格颤介。
// 寬松一致的間距
// 使代碼更易讀
// 使得更加“透氣”
var d = 0,
a = b + 1;
if (a && b && c) {
d = a % c;
a += d;
}
// 反面例子
// 缺失或間距不一
// 使代碼變得疑惑
var d = 0,
a = b + 1;
if (a&&b&&c) {
d=a % c;
a+= d;
}
最后需要注意的一個空格——花括號間距梳星。最好使用空格:
函數、if-else語句滚朵、循環(huán)冤灾、對象字面量的左花括號的前面({)
-
else或while之間的右花括號(})
//{} 空格 if (4) { console.log(1) } else if (3) { console.log(1) } var a = {}
4.命名規(guī)范
- 另一種方法讓你的代碼更具可預測性和可維護性是采用命名規(guī)范。這就意味著你需要用同一種形式給你的變量和函數命名辕近。
- 下面是建議的一些命名規(guī)范韵吨,你可以原樣采用,也可以根據自己的喜好作調整移宅。同樣归粉,遵循規(guī)范要比規(guī)范是什么更重要。
4.1以大寫字母寫構造函數(Capitalizing Constructors)
JavaScript并沒有類漏峰,但有new調用的構造函數:
var adam = new Person();
因為構造函數仍僅僅是函數糠悼,僅看函數名就可以幫助告訴你這應該是一個構造函數還是一個正常的函數。
命名構造函數時首字母大寫具有暗示作用浅乔,使用小寫命名的函數和方法不應該使用new調用:
function MyConstructor() {...}
function myFunction() {...}倔喂、
4.2 分隔單詞(Separating Words)
當你的變量或是函數名有多個單詞的時候,最好單詞的分離遵循統(tǒng)一的規(guī)范靖苇,有一個常見的做法被稱作“駝峰(Camel)命名法”席噩,就是單詞小寫,每個單詞的首字母大寫贤壁。
- 對于構造函數悼枢,可以使用大駝峰式命名法(upper camel case),如MyConstructor()脾拆。
- 對于函數和方法名稱馒索,你可以使用小駝峰式命名法(lower camel case)给梅,像是myFunction(), calculateArea()和getFirstName()。
4.3 注釋(Writing Comments)
你必須注釋你的代碼双揪,即使不會有其他人向你一樣接觸它动羽。通常,當你深入研究一個問題渔期,你會很清楚的知道這個代碼是干嘛用的运吓,但是,當你一周之后再回來看的時候疯趟,想必也要耗掉不少腦細胞去搞明白到底怎么工作的拘哨。
很顯然,注釋不能走極端:每個單獨變量或是單獨一行信峻。但是倦青,你通常應該記錄所有的函數,它們的參數和返回值盹舞,或是任何不尋常的技術和方法产镐。要想到注 釋可以給你代碼未來的閱讀者以諸多提示;閱讀者需要的是(不要讀太多的東西)僅注釋和函數屬性名來理解你的代碼踢步。例如癣亚,當你有五六行程序執(zhí)行特定的任務, 如果你提供了一行代碼目的以及為什么在這里的描述的話获印,閱讀者就可以直接跳過這段細節(jié)述雾。沒有硬性規(guī)定注釋代碼比,代碼的某些部分(如正則表達式)可能注釋 要比代碼多兼丰。
5.css代碼規(guī)范
css規(guī)范我們偉大的張旭鑫老師玻孟,講的很清楚。面向屬性的命名
這是比較好的命名規(guī)范鳍征。簡介來說黍翎,就是我們先定義好一些常用基礎類樣式。組件則使用less,或者sass進行組裝蟆技,形成即可玩敏。