該文于 2013 年創(chuàng)作矢洲,經(jīng)年修改,錄以記之
統(tǒng)一的規(guī)范有助于團(tuán)隊(duì)合作開發(fā)缩焦,但規(guī)范又臭又長(zhǎng)读虏,又不利于閱讀與遵守,所以本規(guī)范盡量壓縮舌界。另外掘譬,本規(guī)范只是草案泰演,還需要在實(shí)踐中不斷完善呻拌。
一、全局規(guī)范
tab or space
考慮到各代碼編輯器的差異睦焕,前端統(tǒng)一采用 4 個(gè)空格 作為縮進(jìn)標(biāo)準(zhǔn)藐握。
設(shè)置 4 個(gè)空格后,向前刪除空格時(shí)就會(huì)很痛苦垃喊,可在編輯器中猾普,設(shè)置按下退格鍵時(shí),一次刪掉前面的四個(gè)空格本谜。
Sublime Text 是這樣設(shè)置的:
"tab_size": 4,
"translate_tabs_to_spaces": true,
VIM 是這樣設(shè)置的:
set autoindent softtabstop=4 shiftwidth=4 expandtab
其他編輯器請(qǐng)自行摸索初家。
UNIX \n or Windows \r\n
統(tǒng)一使用 UNIX 的換行符
文件編碼
統(tǒng)一使用不帶 BOM 字節(jié)的 UTF-8 編碼
路徑和文件名
- 不要在路徑和文件名中使用空格
- view/css/js/img 文件,統(tǒng)一采用陀峰風(fēng)格乌助,請(qǐng)不要用 - 或 _ 連接各個(gè)單詞溜在,更不要不區(qū)分單詞,一律小寫
二他托、HTML 書寫規(guī)范
秉承 HTML5 規(guī)范與精神掖肋,結(jié)合 XHTML,制定本規(guī)范赏参。
基本頁面
采用如下模板:
<!doctype html>
<html lang='zh-CN'>
<head>
<meta charset='utf-8'>
<title>page title</title>
</head>
<body>
</body>
</html>
doctype 采用 html5 建議的 doctype志笼,廢棄 xhtml 的 doctype
-
charset 采用 html5 建議的 charset,不要使用:
<meta http-equiv="charset" content="utf-8">
charset 使用 utf-8
標(biāo)簽閉合
xhtml 規(guī)定把篓,標(biāo)簽必須閉合纫溃,對(duì)沒有內(nèi)容的標(biāo)簽,也要寫一個(gè) /韧掩,例如:
<input type='text' name='username'/>
<br/>
html5 撥亂反正紊浩,回歸 html 本質(zhì),所以,我們這樣寫:
<input type='text' name='username'>
<br>
瘋狂的 html5 甚至允許這樣:
<table>
<tr>
<th>header1
<th>header2
<tr>
<td>cell11
<td>cell12
<tr>
<td>cell21
<td>cell22
<p>content out of table, no table close tag before
<p>second paragraph
我們不要這么瘋狂郎楼,這樣會(huì)弄亂我們的代碼万伤,所以,對(duì)于沒有內(nèi)容的標(biāo)簽呜袁,我們不必用 / 關(guān)閉敌买,對(duì)于有內(nèi)容的標(biāo)簽,我們?nèi)匀蛔駨?xhtml 的要求
屬性和屬性值
html5 允許對(duì)屬性值不加任何引號(hào)阶界,但我們不要這么做虹钮,我們還是要加引號(hào)的,引號(hào)可以用單引號(hào)膘融,也可以用雙引號(hào)芙粱,推薦使用單引號(hào),因?yàn)檩斎腚p引號(hào)時(shí)氧映,要同時(shí)按 Shift 鍵春畔,例如:
<meta charset='utf-8'>
<input type='text' name='username'>
<div class='page cl'>
沒有值的屬性
羅嗦的 xhtml 規(guī)定,對(duì)沒有值的屬性這樣處理:
<input type='radio' checked='checked'/>
但接受 html5 鼓勵(lì)的我們岛都,應(yīng)該這樣寫:
<input type='radio' checked>
常見標(biāo)簽簡(jiǎn)潔寫法
省略 script/link 標(biāo)簽的 type 屬性律姨,這不是必需的,例如:
羅嗦的寫法:
<script src='...' type='text/javascript'></script>
<link href='...' type='text/css' rel='stylesheet'>
簡(jiǎn)潔的寫法:
<script src='...'></script>
<link href='...' rel='stylesheet'>
標(biāo)簽順序
head 標(biāo)簽內(nèi)的標(biāo)簽順序臼疫,推薦:
<meta>
<title>
<link>
script 標(biāo)簽的位置
<script>
應(yīng)放置在 </body>
前择份,而不是 <head>
內(nèi),放在 <head>
內(nèi)的腳本會(huì)阻塞頁面烫堤,導(dǎo)致頁面性能下降
類名和 ID
- 類名使用
-
分割單詞荣赶,不要使用_
或陀峰命名法 - ID 使用陀峰命名法,不要使用
-
或_
例如:
不當(dāng)?shù)念惷?ID
<div class='userBox' id='user_box'>
<div class='user_box' id='user-box'>
恰當(dāng)?shù)念惷?ID
<div class='user-box' id='userBox'>
三鸽斟、CSS 書寫規(guī)范
類名和 ID
-
永遠(yuǎn)不要使用 ID 進(jìn)行樣式化拔创,只使用類名,例如:
// 不當(dāng)?shù)臉邮?#userBox { ... } // 良好的樣式湾盗,使用類名 .user-box { ... }
類名用于樣式化伏蚊,ID 用于腳本訪問
格式風(fēng)格
不當(dāng)?shù)母袷斤L(fēng)格和推薦的格式風(fēng)格對(duì)比:
// 不當(dāng)?shù)模瑊 應(yīng)放在類名后格粪,而不是重啟一行
.user-box
{
...
}
// 應(yīng)該這樣:
.user-box {
...
}
// 不當(dāng)?shù)臉邮?.user-box{
...
}
// 應(yīng)該這樣躏吊,{ 與類名之間應(yīng)該有空格
.user-box {
...
}
// 不當(dāng)?shù)模瑯邮讲粦?yīng)寫在一行:
.user-box { font-size: 14px; line-height: 2; }
// 應(yīng)該這樣:
.user-box {
font-size: 14px;
line-height: 2;
}
// 不當(dāng)?shù)臉邮?font-size:14px;
// 應(yīng)該這樣帐萎,鍵和值應(yīng)以空格隔開
font-size: 14px;
// 不當(dāng)?shù)谋确詈笠恍袠邮揭矐?yīng)以 ; 結(jié)束
.user-box {
font-size: 14px;
line-height: 2
}
// 應(yīng)該這樣:
.user-box {
font-size: 14px;
line-height: 2;
}
// 不當(dāng)?shù)臉邮?.user-list a, .news-list a {
...
}
// 應(yīng)該這樣,每個(gè) selector 占一行
.user-list a,
.news-list a {
...
}
層級(jí)不可過多
// 不當(dāng)疆导,層級(jí)過多
.user-box .user-list .user-item .user-avatar {
...
}
// 應(yīng)該減少層級(jí)赁项,這樣能提高 CSS 性能
.user-box .user-avatar {
...
}
// 采用命名空間的情況下,最好一級(jí)
.user-avatar {
...
}
// 這種情況下,即 .class tag悠菜,可以兩級(jí)
.user-avatar a {
...
}
// 最長(zhǎng)不要超過三級(jí)
使用命名空間
多使用 namespace-*
形式的類名舰攒,每個(gè)業(yè)務(wù)場(chǎng)景定義一個(gè)前綴作為命名空間,如用 site- 作為全站的命名空間悔醋,用 user- 作為用戶中心的命名空間摩窃,用 news- 作為咨詢的命名空間:
.site-top
.user-nav
.news-left
針對(duì) tag 的樣式
-
永遠(yuǎn)不要針對(duì) div/span/p 的樣式,這些標(biāo)簽太通用:
// 不當(dāng)?shù)?.user-box div .news-box span .article-list p
-
可以對(duì) em strong 等含有豐富語義的標(biāo)簽應(yīng)用樣式芬骄,但不要嵌套
// 恰當(dāng)?shù)?.user-list em { ... } // 不當(dāng)?shù)幕福M(jìn)行了多級(jí)嵌套 .user-list li em { ... } // 最不當(dāng)?shù)模瑔为?dú)的 tag 樣式是魔鬼 // 合理的 css reset 除外账阻,全站應(yīng)有且只有一個(gè)統(tǒng)一的 css reset em { ... }
無論如何蒂秘,少用 tag 樣式
注釋
-
less
中允許使用//
注釋,而css
語法中不允許淘太,只能使用/**/
四姻僧、JS 書寫規(guī)范
; 號(hào)的使用
雖然很多正規(guī)的教程,都要求每條 js 語句都應(yīng)以
;
結(jié)束琴儿,但這里段化,我們選擇所有的語句省略;
浪費(fèi)了鍵盤輸入,竊以為是 js 中的糟粕造成,不用糾結(jié),大膽省略吧-
有時(shí)雄嚣,省略
;
會(huì)導(dǎo)致代碼出錯(cuò)晒屎,可加一個(gè)前置;
,例如var str = '' ; [1, 2, 3].forEach(function(n) { console.log(n) })
代碼風(fēng)格
// 所有的二元運(yùn)算符前后應(yīng)有空格
var str = 'hi, ' + name
// 函數(shù)定義與 { 應(yīng)處于同一行缓升,并以空格分割
function lineTo(from, to) {
...
}
// 應(yīng)盡量避免寫 while 與 do .. while 循環(huán)鼓鲁,用 for 替代
// 可以嘗試用 ECMAScript 5 新規(guī)范新增的 Array 方法,徹底替代循環(huán)結(jié)構(gòu)港谊,這些方法有:
// Array.prototype.forEach
// Array.prototype.map
// Array.prototype.every
// Array.prototype.some
// Array.prototype.filter
// Array.prototype.reduce
// Array.prototype.reduceRight
// 這些方法的使用骇吭,符合函數(shù)式理念,一旦習(xí)慣歧寺,就會(huì)愛上這種方式
// for 與 (燥狰,for 內(nèi)的 ; 之間,) 與 { 都應(yīng)有空格
for (var i = 1; i <= 9; i++) {
...
}
// if 與 for 同理斜筐,else 應(yīng)與結(jié)束的 } 處于同一行
if (sex == 'girl') {
...
} else if (sex == 'boy') {
...
} else {
...
}
// 始終在 if/else/for 中使用 {}龙致,那怕只有一條語句
if (condition) {
// only one statement
return true
}
// switch 的 case 應(yīng)該這樣縮進(jìn),default 應(yīng)始終在最后
switch (sex) {
case 'boy':
....
break;
case 'girl':
....
break;
default:
...
}
// 盡量避免使用 switch 語句顷链,可以采用這樣的結(jié)構(gòu)
var map = {
boy: ...,
girl: ...
}
var val = map[sex]
// 甚至可以用 map 執(zhí)行函數(shù)
var actionMap = {
doThis: function() {
...
},
doThat: function() {
...
}
}
// 函數(shù)調(diào)用與 ( 之間不應(yīng)有空格目代,以便和 for if 語句加以區(qū)分
lineTo(5, 6)
// 永遠(yuǎn)不要這樣寫
var arr = new Array()
var obj = new Object()
// 應(yīng)這樣寫
var arr = []
var obj = {}
// 一行一個(gè)變量,應(yīng)該這樣
var v1 = 1
var v2 = 2
// 避免這樣
var v1 = 1, v2 = 2
// 變量名和函數(shù)名采用陀峰樣式,不要使用 _榛了,如
var myVar = 22
// 盡量使用 ES6 的語法在讶,前提是環(huán)境支持(或 webpack + babel 編譯支持,或原生環(huán)境支持)
盡量保持一個(gè)出口
很多人這樣寫代碼:
function someFunc(...) {
if (aCondition) {
...
return aValue
}
if (bCondition) {
...
return bValue
}
...
return cValue
}
這樣的代碼霜大,說實(shí)話真朗,在我眼里,很拙劣僧诚,無論你有千般理由遮婶!什么性能啦、邏輯清晰啦湖笨,都是你對(duì)代碼沒有掌控力的借口旗扑!
不要糾結(jié)于局部的、指令級(jí)的性能慈省,你不是在寫匯編臀防!
多處 return,你是爽了边败,但這種代碼袱衷,很容易出現(xiàn)維護(hù)性問題。
我寧愿多用嵌套 if ... else笑窜,始終保持一個(gè)出口致燥,函數(shù)采用三段式(第一段前置全函數(shù)變量聲明、第二段邏輯處理排截、第三段返回)來寫嫌蚤,例如:
function someFunc(...) {
var result
if (...) {
if (...) {
...
} else {
...
}
} else {
...
}
return result
}
如果嵌套過多,可以將之提取出來断傲,放到一個(gè)新函數(shù)里脱吱,并且,給這個(gè)新函數(shù)认罩,取一個(gè)含義明確的名字箱蝠,這樣代碼就能實(shí)現(xiàn)“自注釋”,例如:
function someFunc(...) {
var result
if (...) {
result = newFuncWithWellDescription(...)
} else {
...
}
return result
}
function newFuncWithWellDescription(...) {
...
}
并且垦垂,應(yīng)在 else 里處理例外情況(錯(cuò)誤處理)宦搬,在 if 里處理正常流程。
不要怕函數(shù)多了影響性能乔外,記住床三,你不是在寫匯編,局部的杨幼、指令級(jí)的性能優(yōu)化是沒有意義的撇簿,系統(tǒng)整體的性能優(yōu)化聂渊,才是重點(diǎn)
變量就地聲明,聲明的同時(shí)進(jìn)行賦值
有些人這樣寫:
function someFunc(items) {
var i, len = items.length
...
for (i = 0; i < len; i++) {
...
}
...
}
這樣寫沒有任何好處四瘫,i 只是一個(gè)循環(huán)變量汉嗽,沒有必要像 C 語言那樣作前置聲明,應(yīng)該直接這樣:
function someFunc(items) {
...
for (var i = 0, len = items.length; i < len; i++) {
...
}
...
}
變量就地聲明并賦值找蜜,賦值后保持不變饼暑,不僅僅符合函數(shù)式編程思想,也是一種良好的編程習(xí)慣
嵌套函數(shù)
嵌套函數(shù)應(yīng)該這樣寫:
function doSomething(...) {
...
var doSomethingInner = function(...) {
...
}
...
}
而不應(yīng)該這樣:
function doSomething(...) {
...
function doSomethingInner(...) {
...
}
...
}
要牢記洗做,在 JS 這類函數(shù)式語言中弓叛,函數(shù)也是值
在 seajs 中,所有的函數(shù)都聲明在 define(function() { ... }) 內(nèi)诚纸,也即撰筷,所有的函數(shù)都是嵌套函數(shù),但這里畦徘,我們忽略 seajs 這一級(jí)毕籽,將 seajs 內(nèi)的第一級(jí)函數(shù),當(dāng)成頂級(jí)函數(shù)井辆,例如:
define(function(require) {
...
function someFunc(...) {
...
}
...
})
而不要這樣:
define(function(require) {
...
var someFunc = function(...) {
...
}
...
})
佛說关筒,不要著相
五、函數(shù)式編程
js 本質(zhì)上是函數(shù)式編程語言杯缺,受當(dāng)時(shí)面向?qū)ο笏汲钡挠绊懻舨ィ舱慈旧弦恍┟嫦驅(qū)ο蟮脑闫伞W屛覀內(nèi)ナ彺孑级崴貧w函數(shù)式本質(zhì)廉赔。
關(guān)于函數(shù)式,可通過搜索引擎進(jìn)行學(xué)習(xí)匾鸥,這里只談函數(shù)式的一個(gè)很重要的特性:不變性。
不變性
簡(jiǎn)單理解碉纳,一個(gè)變量勿负,一旦定義并賦值(定義時(shí)同時(shí)賦值,是一個(gè)良好的編程習(xí)慣)劳曹,其值應(yīng)保持不變奴愉。
看以下代碼:
function onSuccess(data) {
data = parseJson(data)
if (data.s == 200) {
...
}
}
上述代碼存在的問題是,data 的值在第一行發(fā)生了變化铁孵!即使在命令式編程語言中锭硼,這種代碼也是很拙劣的!
一般蜕劝,稍微有經(jīng)驗(yàn)的程序員檀头,無論是否受到過函數(shù)式編程思想的影響轰异,都不會(huì)寫出這樣的代碼。多聲明一個(gè)變量會(huì)死呀暑始?應(yīng)該這樣:
function onSuccess(data) {
var json = parseJson(data)
if (json.s == 200) {
...
}
}
再看以下代碼:
function doubleIt(items) {
for (var i = 0, len = items.length; i < len; i++) {
items[i] = items[i] * 2
}
return items
}
很多命令式編程語言的程序員搭独,覺得上述代碼沒什么,甚至?xí)嬖V你廊镜,這樣節(jié)省內(nèi)存
上述代碼不符合函數(shù)式精神牙肝,items 中的元素,仍然發(fā)生了變化嗤朴,這樣寫:
function doubleIt(items) {
var newItems = []
for (var i = 0, len = items.length; i < len; i++) {
newItems[i] = items[i] * 2
}
return newItems
}
上述代碼配椭,只有循環(huán)變量 i 在變,并且局限在局部雹姊。更徹底地股缸,我們使用 Array.prototype.map:
function doubleIt(items) {
return items.map(function(item) {
return item * 2
})
}
回憶高中時(shí),數(shù)學(xué)中關(guān)于函數(shù)的定義:函數(shù)是一個(gè)集合到另一個(gè)集合容为!這樣的代碼乓序,才算是,真正回歸了坎背,編程語言的數(shù)學(xué)本質(zhì)
不要擔(dān)心函數(shù)調(diào)用的性能替劈,現(xiàn)代的 JS 引擎,會(huì)對(duì)這樣的調(diào)用進(jìn)行優(yōu)化
JS 性能點(diǎn)不在這里得滤,而在 DOM 操作陨献。一個(gè) DOM 操作所耗時(shí)間,約是 JS 對(duì)象操作的 1000 倍以上懂更,所以應(yīng)該盡量減少 DOM 操作
六眨业、補(bǔ)充材料
解讀ECMAScript[1]——執(zhí)行環(huán)境、作用域及閉包
解讀ECMAScript[2]——函數(shù)沮协、構(gòu)造器及原型
閉包漫談(從抽象代數(shù)及函數(shù)式編程角度)
CSS 相對(duì)絕對(duì)定位系列(一)
SeaJS 官方文檔
LESS 中文文檔
使用SeaJS實(shí)現(xiàn)模塊化JavaScript開發(fā)
七龄捡、擁抱 ES6+
從現(xiàn)在開始,重視并書寫 ES6+慷暂,這里有個(gè)很好的教程 http://es6.ruanyifeng.com/