本文地址: http://www.hicss.net/evolve-your-javascript-code/javascript
方纔在程序裏看到一段JS代碼,寫法極爲高明,私心想着如果其按照規範來寫,定可培養對這門語言的理解,對JS編程能力提升必是極好的。說人話:丫代碼寫的太亂,看的窩火!css
最近閒暇無事,準備對本身JS學習作一個總結。衆所周知,JS是一種語法極其靈活的語言,一千我的會有一千種JS書寫方式。這形成的結果每每就是給項目往後的開發及維護留下一個不小的隱患,也對你和團隊再次開發及閱讀代碼形成必定困難,我的認爲良好的書寫規範是應該首當其衝的。因此參考一些優秀前端開發團隊的代碼規範後,總結了幾點,但願能讓你的Javascript代碼上升一個臺階。前端
變量命名:java
變量名包括全局變量,局部變量,類變量,函數參數等等,他們都屬於這一類。正則表達式
變量命名都以類型前綴+有意義的單詞組成,用駝峯式命名法增長變量和函式的可讀性。例如:sUserName,nCount。編程
前綴規範:數組
每一個局部變量都須要有一個類型前綴,按照類型能夠分爲:app
s:表示字符串。例如:sName,sHtml;
n:表示數字。例如:nPage,nTotal;
b:表示邏輯。例如:bChecked,bHasLogin;
a:表示數組。例如:aList,aGroup;
r:表示正則表達式。例如:rDomain,rEmail;
f:表示函數。例如:fGetHtml,fInit;
o:表示以上未涉及到的其餘對象,例如:oButton,oDate;
g:表示全局變量,例如:gUserName,gLoginTime;dom
固然,也能夠根據團隊及項目須要增長前綴規範,例如咱們團隊會用到:函數
$:表示Jquery對象。例如:$Content,$Module;
一種比較普遍的Jquery對象變量命名規範。
j:表示Jquery對象。例如:jContent, jModule;
另外一種Jquery對象變量命名方式。
fn:表示函數。例如:fnGetName,fnSetAge;
和上面函數的前綴略有不一樣,改用fn來代替,我的認爲fn可以更好的區分普通變量和函數變量。
dom:表示Dom對象,例如:domForm,domInput;
項目中不少地方會用到原生的Dom方法及屬性,能夠根據團隊須要適當修改。
這裏能夠根據項目及團隊須要,設計出針對項目須要的前綴規範,從而達到團隊開發協做便利的目的。
例外狀況:
1:做用域不大臨時變量能夠簡寫,好比:str,num,bol,obj,fun,arr。
2:循環變量能夠簡寫,好比:i,j,k等。
3:某些做爲不容許修改值的變量認爲是常量,所有字母都大寫。例如:COPYRIGHT,PI。常量能夠存在於函數中,也能夠存在於全局。
爲何須要這樣強制定義變量前綴?正式由於javascript是弱語言形成的。在定義大量變量的時候,咱們須要很明確的知道當前變量是什麼屬性,若是隻經過普通單詞,是很難區分的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
//普通代碼
var
checked =
false
;
var
check =
function
() {
return
true
;
}
/**
some code
**/
if
(check) {
//已經沒法很確切知道這裏是要用checked仍是check()從而致使邏輯錯誤
//do some thing
}
//規範後代碼
var
bChecked =
false
;
var
fnCheck =
function
() {
return
true
;
}
/**
some code
**/
if
(bChecked) {
// do some thing
}
if
(fnCheck()) {
// do other thing
}
|
函數命名:
統一使用動詞或者動詞+名詞形式,例如:fnGetVersion(),fnSubmitForm(),fnInit();涉及返回邏輯值的函數可使用is,has,contains等表示邏輯的詞語代替動詞,例如:fnIsObject(),fnHasClass(),fnContainsElment()。
若是有內部函數,使用_fn+動詞+名詞形式,內部函數必需在函數最後定義。例如:
1
2
3
4
5
6
7
8
9
10
11
12
|
function
fnGetNumber(nTotal) {
if
(nTotal < 100) {
nTotal = 100;
}
return
_fnAdd(nTotal);
function
_fnAdd(nNumber) {
nNumber++;
return
nNumber;
}
}
alert(fGetNumber(10));
//alert 101
|
對象方法與事件響應函數:
對象方法命名使用fn+對象類名+動詞+名詞形式;例如 fnAddressGetEmail(),主觀以爲加上對象類名略有些雞肋,我的認爲一個對象公開的屬性與方法應該作到簡潔易讀。多增長一個對象類名單詞,看着統一了,但有點爲了規範而規範的味道,這裏根據自身喜愛仁者見仁智者見智吧。
某事件響應函數命名方式爲fn+觸發事件對象名+事件名或者模塊名,例如:fnDivClick(),fnAddressSubmitButtonClick()
補充一些函數方法經常使用的動詞:
get 獲取/set 設置, add 增長/remove 刪除
create 建立/destory 移除 start 啓動/stop 中止
open 打開/close 關閉, read 讀取/write 寫入
load 載入/save 保存, create 建立/destroy 銷燬
begin 開始/end 結束, backup 備份/restore 恢復
import 導入/export 導出, split 分割/merge 合併
inject 注入/extract 提取, attach 附着/detach 脫離
bind 綁定/separate 分離, view 查看/browse 瀏覽
edit 編輯/modify 修改, select 選取/mark 標記
copy 複製/paste 粘貼, undo 撤銷/redo 重作
insert 插入/delete 移除, add 加入/append 添加
clean 清理/clear 清除, index 索引/sort 排序
find 查找/search 搜索, increase 增長/decrease 減小
play 播放/pause 暫停, launch 啓動/run 運行
compile 編譯/execute 執行, debug 調試/trace 跟蹤
observe 觀察/listen 監聽, build 構建/publish 發佈
input 輸入/output 輸出, encode 編碼/decode 解碼
encrypt 加密/decrypt 解密, compress 壓縮/decompress 解壓縮
pack 打包/unpack 解包, parse 解析/emit 生成
connect 鏈接/disconnect 斷開, send 發送/receive 接收
download 下載/upload 上傳, refresh 刷新/synchronize 同步
update 更新/revert 復原, lock 鎖定/unlock 解鎖
check out 簽出/check in 簽入, submit 提交/commit 交付
push 推/pull 拉, expand 展開/collapse 摺疊
begin 起始/end 結束, start 開始/finish 完成
enter 進入/exit 退出, abort 放棄/quit 離開
obsolete 廢棄/depreciate 廢舊, collect 收集/aggregate 彙集
上面討論了基本的JS書寫命名規範,按我我的見解,只要可以按照上面的規範寫出來的代碼,通常不會太糟糕,最不濟沒人會說你代碼亂的難以閱讀。代碼規範對於大團隊的維護建設是毋庸置疑的,固然對於我的的代碼素養也是頗有幫助的,但願你可以經過上文可以增強你的代碼規範,寫出易讀易維護的代碼。
代碼的規範屬於戰術上的進化,下面咱們再來看看如何讓你的Javascript在戰略上進化。
面向對象書寫Javascript
面向對象書寫Javascript想必你必定不會陌生,但我敢說,大多數前端通常不會經過面向對象來書寫JS代碼。第一是Javascript自己的語言機制緣由形成面向對象的書寫困難,因爲Javascript是原型式繼承又是一個類C的語言,他的面向對象的東西相對於C++/Java比較奇怪。第二是Javascript做爲一種語法極其靈活的語言,直接致使了面向對象書寫JS又有多種寫法,讓許多初學者分不清到底哪一個纔是正確的寫法。基於上述和我的的經驗推薦以下倆種書寫方式:
第一類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
(
function
(){
function
Circle(nRadius){
this
.nR = nRadius;
}
Circle.prototype = {
PI : 3.14,
fnGetArea :
function
(){
return
this
.PI *
this
.nR *
this
.nR;
}
}
var
c1 =
new
Circle(5);
alert(c1.fnGetArea());
//78.5
})();
|
上面這種能夠說是很標準的面向對象JS書寫方式了咱們又稱之爲工廠模式,優勢就是簡單容易上手,新手經常喜歡這麼寫。以上代碼略微作些改動,會有以下這個變種:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
(
function
(){
function
Circle(nRadius, sMessage){
this
.init.apply(
this
, arguments);
}
Circle.prototype = {
init :
function
(nRadius, sMessage){
this
.nR = nRadius;
this
.sMessage = sMessage;
},
PI : 3.14,
fnGetArea :
function
(){
return
this
.sMessage +
": "
+
this
.PI *
this
.nR *
this
.nR;
}
}
var
c =
new
Circle(5,
"構造初始化 面積"
);
alert(c.fnGetArea());
//構造初始化 面積: 78.5
})();
|
上面這個變種,就比較有意思了,this.init.apply(this, arguments);這行代碼把初始化的任務交接給了init()方法,這麼作的好處就是能夠把全部初始化的東西都放在一個地方進行,增長可閱讀性,須要理解必定的Javascript運行機制原理。
第二類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
(
function
(){
function
Circle(){
}
Circle.prototype = {
init :
function
(nRadius, sMessage){
this
.nR = nRadius;
this
.sMessage = sMessage;
},
PI : 3.14,
fnGetArea :
function
(){
return
this
.sMessage +
": "
+
this
.PI *
this
.nR *
this
.nR;
}
}
var
c =
new
Circle();
c.init(5,
"手動構造初始化 面積"
);
alert(c.fnGetArea());
//手動構造初始化 面積: 78.5
})();
|
這類寫法是我如今比較喜歡的寫法,簡潔高效。從書寫角度來看省去了構造函數初始化屬性,改用其init()中初始化。另外一個好處在於不在new Circle()構造之初傳入參數,我的認爲當new構造對象的時候,最好不要摻雜參數,這樣作很「危險」,改成「手動型」初始化更易於代碼排查修改。固然這種寫法還有一個緣由就是他能夠很好的轉換成通常前端接受的「封裝型」代碼,咱們把上面的代碼也略微改動一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
(
function
(){
var
Circle = {
init :
function
(nRadius, sMessage){
this
.nR = nRadius;
this
.sMessage = sMessage;
},
PI : 3.14,
fnGetArea :
function
(){
return
this
.sMessage +
": "
+
this
.PI *
this
.nR *
this
.nR;
}
}
Circle.init(5,
"封裝型 面積"
);
alert(Circle.fnGetArea());
//封裝型 面積: 78.5
})();
|
是否是對上面這類代碼很熟悉,不少網站上的例子都是使用這類封裝型代碼書寫的,優勢是代碼的封裝性良好,能夠有效的重用,多用於頁面功能性效果實現,封裝一個Tab控件、封裝一個跑馬燈效果等等。缺點就是不能很好的用做繼承,這是和上面三種格式最大區別。可話又說回來通常JS代碼不多會用到繼承的地方,除非是寫一個大型庫(相似YUI)會用到繼承外,通常寫一個功能模塊用到封裝型代碼就夠用了,因此大多數前端喜歡使用這類封裝型的書寫風格。
上面介紹了2類4種面向對象的寫法,通常面向對象書寫格式基本都在上面了,熟悉面向對象書寫能夠有效的增長你對JS的理解。熟練使用上面4中寫法也可以很好的在工做中給代碼維護修改帶來便利。最後咱們再來談一個技巧,讓你的Javascript代碼在技巧上進化。
用對象字面量構造對象
一個對象字面量就是包含在一對花括號中的0個或多個「名/值」對。上文在面向對象書寫格式的時候咱們就大量的使用了對象字面量的書寫格式。
對象字面量書寫Javascript能夠很好的簡化代碼,又能極大的增長代碼可讀性,尤爲做爲參數使用能夠有化腐朽爲神奇的表現。咱們看下面代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
(
function
(){
function
Person(sName, nAge, nWeight, bSingle){
this
.sName = sName;
this
.nAge = nAge;
this
.nWeight = nWeight;
this
.bSingle = bSingle;
}
Person.prototype.showInfo =
function
(){
return
this
.sName +
" "
+
this
.nAge +
" "
+
this
.nWeight +
" "
+
this
.bSingle;
}
var
p =
new
Person(
"海玉"
, 25, 75,
true
);
alert(p.showInfo());
//海玉 25 75 true
})();
|
上面是一個很標準的工廠模式,通常而言這類寫法屬於那種規規矩矩沒有大錯也沒有亮點的代碼,並且參數很多,一個不當心還會傳入錯誤的參數,而應用對象字面量技巧能夠很好的規避此類問題,咱們來看改動事後的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
(
function
(){
function
Person(){
}
Person.prototype = {
init :
function
(option){
if
(
typeof
option ==
"undefined"
){
option = {};
}
this
.sName = option.sName ||
"海玉"
;
this
.nAge = option.nAge || 25;
this
.nWeight = option.nWeight || 75;
this
.bSingle = (
typeof
option.bSingle !=
"undefined"
) ? option.bSingle :
true
;
},
showInfo :
function
(){
return
this
.sName +
" "
+
this
.nAge +
" "
+
this
.nWeight +
" "
+
this
.bSingle;
}
}
var
p =
new
Person();
p.init({
nWeight : 80,
sName :
"Hank"
})
alert(p.showInfo());
//Hank 25 80 true
})();
|
這裏使用第三種面向對象寫法,有興趣的朋友能夠自行嘗試改爲封裝型寫法。上面的改寫看出哪裏改動最大嗎?對的,傳入參數改爲了一個對象字面量,並且傳入參數能夠是隨意設置,位置顛倒也不會有任何問題。這裏充分利用對象字面量優勢,利用鍵值對代替原始的傳參方式大大提高了可讀性和容錯性。還有一個改進就是默認值的處理,若是沒有傳入任何參數,此代碼也能很好的運行下去,不會有任何問題。
注1:這裏形參命名爲option,沒有遵照上面的變量命名規範是爲了方便書寫與閱讀,具體狀況具體分析。
注2:因爲||運算符對於布爾值默認賦值會出現賦值問題,因此要先進行判斷是否爲undefined,再利用三元運算符能夠很好的完成布爾值的默認值賦值。
總的來講上面闡述的代碼進化只能算的上是「修身」層面,想要真正的讓代碼「修心」仍是得多寫,多看,多練。只有這樣你的代碼纔會更精練,更有擴展性,更好的維護性。
林林總總寫了這些個總結,一來是對本身學習的記錄,二來也是想同你們探討JS還有哪些地方有潛力可挖,腦子裏還有許多零零碎碎的片斷,待往後再次整理驗證再與你們一塊兒分享吧。