"程式設計風格"的選擇不應該基于個人愛好、熟悉程度、打字工作量等因素,而要考慮如何盡量使代碼清晰易讀、減少出錯。你選擇的,不是你喜歡的風格,而是一種能夠清晰表達你的意圖的風格。這一點,對于Javascript這種文法自由度很高、設計不完全成熟的語言尤其重要。
程式員固然可以自由選擇程式設計風格,但是好的程式設計風格有助于寫出品質更高、錯誤更少、更易于維護的程式。下面以點概面,通過一些實際例子來簡要概述一下好的程式設計風格
命名
JavaScript語言的核心ECMAScript,遵照了駝峰是大小寫:即命名法是由小寫字母開始的,後續每個單詞首字母都大寫
變量和函數的命名:變量名應該總是遵循駝峰大小寫命名法,并且明明字首是個名詞;函數也是駝峰式命名(pascal Case),但是字首應當是個動詞。這樣便于區分普通變量和函數變量。同時要注意,不要取一些毫無意義的名詞(除了在循環中經常出現的i,j,k..),注入foo,bar,tmp之類随便拿來用的名字,如果不看上下文實在不好了解變量的本來目的。
//好的寫法
var count = 10;
var myName = 'feng';
var found = true;
function getName() {
return myName;
}
//不好的寫法
var getCount = 10;//看起來像函數
var isFound = 10;//你是不是經常這麼用
//看起來像變量
function theName() {
return myName;
}
常量的命名:很長一段時間JavaScript中并沒有真正意義上的常量,但是這并不能阻止開發者将變量用作常量。為了區分普通變量和常量,參考C語言,一種約定應運而生:使用大寫字母和下劃線來命名,下劃線用于分隔單詞。
var MAX_COUNT = 10;
var URL = "http://m.jinhui365.com";
if(count < MAX_COUNT){
doSomething();
}
構造函數的命名:JavaScript中,構造函數隻不過是前面冠以new預算符的函數,用來建立對象,上文提到過普通函數的命名采用的駝峰式。這裡,構造函數的命名法也采用駝峰式,隻不過是大駝峰,用來與普通函數做區分。大函數指在小駝峰基礎上,将第一個字元替換成大寫如(Pascal Case),還有構造函數的命名也常常是名詞。是以,按照我們的“約定”,如果你看到大駝峰命名的函數名是名詞,但是沒有構造函數的話,肯定能瞬間定位到問題
function Person(name) {
this.name = name;
}
var me = Person("feng");// new Person()
案例一:重構一個代碼風格混亂的功能函數:有一個需求,通過一系列toggle顯示的折疊塊顯示内容。背景傳來的字段為如下格式
[{"title":"投資目的","content":"”茅台增值回購351215”是以500ml 瓶裝53°飛天茅台酒為标的,在貴州白酒交易所平台挂牌,并通過金彙平台進行直銷的系列酒産品。"},
"title":"提酒優勢","content":"每個名義周期開始起5日到結束前5日之間都可以提酒。認購後,無論茅台酒市場價格如何變動,持有到期,您都将獲得約定回報。",
"title":"轉讓優勢","content":"我是轉讓規則"]
實作如下效果:每個{}中的内容作為一條資訊:title作為标題,content作為描述文字;初始時隻顯示标題;點選任何一個title,展示對應的content;點選其他title則關閉目前content,展開另一個content;再次點選目前content,則隻隐藏目前content
實作代碼如下,首先是html代碼
<!--周遊explain數組-->
<% for(var i = 0; i < explain.length; i++) { %>
<div>
<!--每個{}中的title:包含文字和右側的箭頭(箭頭會随折疊變動防線)-->
<div id="explain-title-<%= i %>" οnclick="togg(<%= i%>)">
<div style="float: left;"><%= explain[i].title %></div>
<span class="btn_gd"></span>
</div>
<!--每個{}中的content-->
<div id="explain-content-<%= i %>">
<%- explain[i].content %>
</div>
</div>
<% } %>
下面看一下javaScript代碼。主要的邏輯就是先将所有的content隐藏。當所有的content都關閉時,點選title則展示目前對應的content,設定temp為目前索引I;如果點選content時,如果不是所有的content都關閉則進行判斷,目前的索引i是否等于temp,即點選的是否是目前的title,如果是目前的title則隻進行關閉操作,如果點選的不是目前的title,則意味着點選的是一個新的title,則關閉上一個,打開新的content;
有兩個缺點:1 變量命名不規範,含義不明确 2.重複性代碼過多
<script>
var flag=true;
var temp;
function togg(i) {
var target = '#explain-content-' + i;
var target_title ='#explain-title-' + i;
var old_target_title ='#explain-title-' + temp;
if(flag){
$('.explain-content').hide();
$(target_title).removeClass('bottom-border')
$(target_title).find('span').attr('class','btn_gu')
$(target).show();
flag=false;
temp=i;
}else{
if(temp == i){
$('.explain-content').hide();
$(old_target_title).addClass('bottom-border')
$(old_target_title).find('span').attr('class','btn_gd')
flag=true;
}else{
var new_target = '#explain-content-' + i;
var new_target_title = '#explain-title-' + i;
$('.explain-content').hide();
$(old_target_title).addClass('bottom-border')
$(old_target_title).find('span').attr('class','btn_gd')
$(new_target_title).removeClass('bottom-border')
$(new_target_title).find('span').attr('class','btn_gu')
$(new_target).show();
temp=i;
flag=false;
}
}
}
</script>
然後看一下經過調整之後的代碼
1.首先用currentOpened 和allHasClosed來替換掉意義模糊的temp和flag;
2.将邏輯進行拆分:打開第i個,關閉第i個,分别封裝成不同的函數
<script type="text/javascript">
function open(i) {
var target = '#explain-content-' + i;
var target_title = '#explain-title-' + i;
$(target_title).removeClass('bottom-border')
$(target_title).find('span').attr('class', 'btn_gu')
$(target).show();
currentOpened = i;
}
function close(i) {
var target_title = '#explain-title-' + i;
$(target_title).addClass('bottom-border')
$(target_title).find('span').attr('class', 'btn_gd')
$('.explain-content').hide();
}
var currentOpened;
var allHasClosed = true;
function togg(curentClick) {
if (allHasClosed) {
open(curentClick);
allHasClosed = false;
} else {
close(currentOpened);
if (currentOpened == curentClick) {
allHasClosed = true;
} else {
open(curentClick);
}
}
}
</script>
語句和表達式
花括号的對齊:分為兩種:第一種風格是将花括号放置在塊語句中第一句代碼的末尾,比如
if (condition) {
doSomething();
} else {
doSomethingElse();
}
第二種風格是将做花括号放置在塊語句首航的下一行,比如
if (condition)
{
doSomething();
}
else
{
doSomethingElse();
}
推薦第一種對齊方式,因為不僅代碼看起來緊湊,同時也會避免JavaScript自動給行尾添加‘;’帶來的錯誤,比如下面的代碼采用第二種對齊方式,原意是傳回一個對象,實際上傳回的是一個undefined值
return
{
key : value;
};
因為javascript實際上會給return後面添加分号,使一個return語句變成了reuturn;
不要省略行尾分号;就這麼簡單,而且是永遠不要
避免使用with語句:無法辨識出with代碼塊中的屬性的歸屬,同時javascript引擎和壓縮工具無法對這段代碼進行優化
for 與 for-in循環:乍一看很相似,但是卻有着完全不同的用法。
傳統的fo循環用于周遊數組成員,比如
var values = [ 0, 1, 2, 3, 4, 5],
i,len;
for (i=0, len=values.length; i < len; i++) {
alert(values[i]);
}
而for-in循環是用來周遊對象屬性的,普遍禁止用來便利數組。不用定義任何控制條件,循環會有條不紊的周遊每個對象屬性,并傳回屬性名而不是值,比如:
var prop;
for (prop in object) {
console.log("Property name is " + prop);
console.log("Property value is " + object[prop]);
}
但是for-in循環有一個問題,就是它不僅周遊對象的執行個體屬性,同樣還周遊從原型繼承來的屬性。當周遊自定義對象的屬性石,往往會因為意外的結果而終止,處于這個原因,最好使用hasWwnProperty()來為 for-in 循環過濾出執行個體屬性,比如
var prop;
for (prop in object) {
if(object.hasOwnProperty(prop)){
console.log("Property name is " + prop);
console.log("Property value is " + object[prop]);
}
}
變量函數和運算符
變量聲明:javascript中存在變量聲明提前的概念:在函數内部任意地方定義變量何在函數頂部定義變量是完全一樣的,是以,一種好的風格是将所有變量聲明放在函數頂部而不是散落在各個角落。同時,使用單var進行變量聲明,比如
function doSomethingWithItems(items) {
var i, len,
value = 10,
result = value + 10;
for (i=0, len=items.length; i < len; i++) {
doSomething(items[i]);
}
}
和變量聲明一樣,函數聲明也會被JavaScript引擎提前。是以,代碼中函數的調用可以出現在函數聲明之前。但是一種好的風格是,先聲明函數然後使用函數,而且函數内部的局部函數應當緊接着變量聲明之後聲明。
相等:JavaScipt 具有強制類型轉換機制,且這個機制是很微妙的,對于某些運算來說,為了取得成功的結果,強制類型轉換會驅使某種類型的變量自動轉換成其他不同類型,進而往往造成意想不到的結果。發生強制類型轉換最常見的場景就是,使用了判斷相等運算符 == 和!=的時候。當要比較兩個值得類型不同是,這兩個運算符都會有強制類型轉換。但是很多實際情況中,代碼并不按照我們所期望的方式運作。
PS:相等操作符會對操作值進行隐式轉換後進行比較:
1)如果一個操作值為布爾值,則在比較之前先将其轉換為數值
2)如果一個操作值為字元串,另一個操作值為數值,則通過Number()函數将字元串轉換為數值
3)如果一個操作值是對象,另一個不是,則調用對象的valueOf()方法,得到的結果按照前面的規則進行比較
4)null與undefined是相等的
5)如果一個操作值為NaN,則相等比較傳回false
6)如果兩個操作值都是對象,則比較它們是不是指向同一個對象
是以,我推薦所有的判斷相等都采用 === 和 !== ,不僅判斷類型同時也判斷數值上的相等,避免了強制類型轉換帶來的模棱兩可的結果判定
未完待續~