恢復內容開始 這篇文章很長,但的確是一篇非常乾的乾貨,講訴了 HTML、JavaScript、CSS、jQuery使用的一些規範與建議,前端的同學可以認真閱讀此文,並比較自己平時的一些習慣,看是否有改進的地方…… HTML 咋的啦,DOCTYPE? 不定義DOCTYPE是一種可以被判死刑的罪行。 以 ...
---恢復內容開始---
這篇文章很長,但的確是一篇非常乾的乾貨,講訴了 HTML、JavaScript、CSS、jQuery使用的一些規範與建議,前端的同學可以認真閱讀此文,並比較自己平時的一些習慣,看是否有改進的地方……
HTML
咋的啦,DOCTYPE?
不定義DOCTYPE是一種可以被判死刑的罪行。 以前你可能用的是下麵的DOCTYPE,不過你要知道現在已經有更簡潔清晰的代碼取而代之了。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
理想的狀況是用HTML5 DOCTYPE,所有現代的瀏覽器都支持它,即使是不支持HTML5的瀏覽器,例如IE6,IE7,也會由此轉入標準模式。 參見來源。
<!DOCTYPE html>
編寫合法且語義清晰的標記
用整潔、語義清晰的HTML編寫網站代碼是我們一直孜孜以求的。有時我們會發現前人配置頁面的方式限制了我們,或者有時我們編寫的是HTML格式的email模板。但永遠不要偏離HTML的規範,即使是為瞭解決特定瀏覽器相容性的bug。所有的標題應該從<h2>
開始分層級創建,文欄位落應該總是放在<p>
標簽里,諸如此類。如果你編寫的HTML的語義清晰,產生的頁面會更整潔、簡練,而且易於被搜索引擎爬蟲解析。這是你能做到的最簡單的SEO修補方式。來看看下麵的段落,你覺得哪個更整潔?是這個?
<span class="sectionHeading">A Heading</span>
<br /> <br />
Lorem ipsum dolor sit amet. ...
<br /> <br />
還是這個?
<h2>A Heading</h2>
<p>
Lorem ipsum dolor sit amet. ...
</p>
滑鼠中鍵點擊的應變方式
現代web應用最令人鬱悶的可用性缺陷之一是超鏈接功能的變種。 一些看起來像是超鏈接的元素可能是通過Javascript映射的單擊功能,這就破壞了滑鼠中鍵點擊(在新的tab中打開鏈接頁面)的功能。即使它們能在新的標簽頁打開,它們只帶有一個 # 的href又會把你帶回到同樣的頁面。深刻詮釋了該問題的一個現代熱門網站的例子就是Twitter。在它的整個應用里,滑鼠中鍵點擊用戶名或頭像會得到完全不同的結果。
<!-- 舊的方式,破壞網頁語義 -->
<a href="#"></a>
<!-- 如果滑鼠點擊不能產生一個頁面,那就不是超鏈接 -->
<span class="link" role="link"></span>
另一個替代方案是使用 # 引導的路徑,它會把普通的url映射為 # 引導的鏈接,然後通過AJAX來獲取頁面片段。提供此功能的庫應該能夠在滑鼠中鍵點擊的時候正常顯示頁面,或者在左鍵點擊時把該頁面內容載入到指定的區域。不過這樣也要慎重行事,有很多人都認為 鏈接正在破壞web應用
用Microformats格式表示聯繫人信息
Microformat是一種便於機器讀取聯繫人信息的方式。hCard類(不是vCard)用來定義元素里包含的內容類型。這些內容會被瀏覽器提取並突出顯示。
<span class="tel">
<span class="type">home</span>:
<span class="value">+1.415.555.1212</span>
</span>
如果你曾經瀏覽採用此格式的網頁,你會註意到類似skype的程式可以輕鬆檢測到網頁上的哪些數字是電話號碼。在iOS設備上的Safari瀏覽器也可以做到類似的事情。
有關Microformat的更多信息請參閱http://microformats.org/wiki/hcard
圖片需要設 'Alt' 文本
<img>
標簽需要 alt 文本,以便檢查並滿足可讀性的要求。 在 alt 屬性中的文本必須能夠說明圖片顯示的內容或要達到的效果,除非該圖片不重要。
如果圖片只是一個列表中的著重號或者其他無關緊要的圖標,最好是給 alt 屬性一個空字元串,但還是留著它。這樣屏幕閱讀器會忽略它,而不是把"著重號"連讀20次。
<img src="dog.gif" alt="Fido and I at the park!" />
<!-- 很好,描述清晰 -->
<img src="bullet.gif" alt="bullet" />
<!-- 不好,顯得多餘 -->
<img src="bullet.gif" alt="" />
<!-- 好 -->
只對錶格數據用table標簽
table標簽永遠只應該用在表格數據的展示上。唯一的例外是當編寫HTML格式的郵件時,這種情況下可能table是某些坑爹的郵件客戶端唯一支持的樣式了。
為了可讀性,表格頭永遠要使用 <th>
元素。同時切記要設置cellpadding, cellspacing 和 border 的值為 0 , 因為這些樣式由CSS來控制更容易確保一致性。
<table cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>
<th>
Cell Header
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Cell Item
</td>
</tr>
</tbody>
</table>
使用 jQuery 和 jQuery UI Widgets
jQuery 和 jQuery UI 被做成儘可能在不同瀏覽器上表現出幾乎相同的外觀和功能。 jQuery UI 被設計為符合 WAI WCAG 2.0 及 WAI ARIA, 因此採用該框架可以避免在你的站點上運行的插件或腳本的所有不確定性。
JavaScript代碼留空和格式
任何關於代碼格式、留空和大括弧位置的討論都會引起激烈辯論。對此,我想最簡單的規則就是,除非你願意把整個代碼文件重新格式化,不然還是尊重並保持已有代碼文件的格式。這意味著如果你看到一個JS文件里的大括弧沒有換行寫,那你的代碼也要繼續保持大括弧不換行。如果你的代碼沒有和代碼文件里的其他部分保持一致,那麼你的代碼就不應該通過代碼審查流程。
一致的代碼格式讓代碼更加易讀,同時也意味著代碼容易用查找/替換命令進行修改。謝天謝地,我們自己形成的編程習慣和jQuery正式推薦的方式非常相似。細微的差異也是有的,不過,那些是個人問題或者我們覺得沒法維護的一些東西。 參閱jQuery核心樣式指南
字元間距
// 不好
if(blah==="foo"){
foo("bar");
}
// 好 :)
if (blah === "foo") {
foo("bar");
}
大括弧不換行
// 不好
if (foo)
{
bar();
}
// 好 :)
if (foo) {
bar();
}
總是用大括弧
// 不好
if (foo)
bar();
// 好 :)
if (foo) {
bar();
}
字元串處理
引用字元串永遠要用雙引號。 有些人非常喜歡用C語言風格的字元串(單引號),但這種習慣會導致腳本內部的風格衝突。C語言風格的字元串處理要求空字元串和單字元包在單引號里,而短語和單詞必須包在雙引號內。
註釋
往代碼里玩命加註釋的需求是由各種經理、主管及其他很少接觸代碼的人們引領的。這種需求無非是雇員們考核指標中的一個勾選欄,花在這上面的時間基本沒有帶來什麼回報。 如果那些從善如流的開發者能遵循本文檔中提出的建議,他們的代碼會變得相當易於閱讀,一目瞭然,以至於再用註釋描述這些代碼會多餘得令人尷尬。來看下麵的例子。在這裡,布爾變數被作為問題提出,而函數也有直觀的命名。
if (user.hasPermission) {
editPage();
}
至少在這個場景中,註釋是完全沒有必要的。
註釋重要的情況
一個項目里,永遠會有某些部分難以查閱和理解。比如一個複雜的正則表達式,或者一個對角度進行計算或在度和弧度單位之間切換的數學函數。沒有上面的註釋,初級或中級的讀者將對腳本的含義毫無頭緒。
// 校驗美國電話號碼的正則表達式,號碼格式是 (XXX) XXX-XXXX (減號、空格和括弧都是可選的,可以有也可以沒有)
var phoneRegEx = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
總是使用 === 比較符
使用 == 比較符可以讓令人鬱悶的bug消失於無形。它允許在 JavaScript花園 中有清楚解釋的弱類型。使用嚴格的 === 比較符不會執行類型強制轉換,從而能夠嚴格地評估兩個對象之間的差別。再說一遍,更多詳細信息請參見 JavaScript花園。
var zeroAsAString = "0";
if (zeroAsAString == 0) {
// 這樣也能判斷為true,呵呵...
}
if (zeroAsAString === 0) {
// 判斷為false
}
例外
在和null進行比較的時候,允許使用 == 比較符,因為它會檢測null和undefined兩個屬性。如果你不完全理解這個原理,那我還是建議你用 === 比較符為好。
var foo = null;
// foo 是 null, 但 bar 是 undefined ,因為它尚未被聲明
if (foo == null && bar == null) {
// 上面的判斷還是成立的
}
使用 .parseInt() 的時候,總是指定第二個 'radix' 參數
把字元串解析為整數的時候,有個好習慣是指定第二個基數參數 -- 它會確定該字元串被轉換成幾進位。當字元串以 0 開頭的時候,預設情況下會觸發 16 進位作為基數。大部分初級和中級用戶只會用到 10 這個基數。 感謝 João Moreno 記錄的這個 勘誤。
alert( parseInt("08") ); // alerts: 2
alert( parseInt("08", 10) ); // alerts: 8
避免直接true 和 false
直接比較 true 和 false 的值是沒有必要的。有時候也許明確一下有好處,但它還是額外的代碼。
if (foo === true) {
// 用了 === 倒是不錯,可這是多餘的
}
if (foo) {
// 贊!
}
if (!bar) {
// 反過來也贊
}
避免污染全局命名空間
過分依賴全局變數是我們組所有人 -- 特別是我自己 -- 特別有負罪感的一件事。關於為啥全局變數不好的討論是相當直接的:這增加了腳本和變數衝突的概率,而且源文件和命名空間本身都會充斥著數不清的命名模糊的變數。
Douglas Crockford 堅信一個Javascript應用的代碼質量可以用其中使用的全局變數數來評價,越少越好。由於並不是什麼都可以定義成local的(不過要誠實,其實你現在考慮的那個是可以的,別偷懶),你需要想辦法整理你的變數以避免衝突,並把命名空間的膨脹減到最小。最簡單的方法就是採用單變數或者把用到這些全局變數的模塊儘可能減少。 Crockford提到YUI只用了一個全局變數,YAHOO。他在他的博文 "全局統治" 中討論了更多的細節問題。
考慮這種情況:對於小型web應用,全局變數通常用於保存應用級的設置,可以用你的項目名或者settings作為命名去定義一個對象,這樣總的來說會更好。
// 被污染的全局命名空間
var settingA = true;
var settingB = false;
var settingC = "test";
// 用 settings 作為對象命名
var settings = {
settingA: true,
settingB: false,
settingC: "test"
}
不過,如果我們可以通過避免使用全局變數來減少衝突概率,但是把命名空間標準化成一樣的,豈不是會增加各個應用之間產生衝突的概率麽?呃,這個擔憂確實有道理。所以,建議你用自己特定的應用名作為全局變數的命名空間,或者用和jQuery採取的 $.noConflict() 模式相同的方法重新分配你的命名空間.
駝峰法變數命名
JavaScript變數的駝峰法命名在大部分編程環境中都是作為標準的。有讀者在評論中提出了唯一的例外,就是要用大寫字母加下劃線來指代常量。
var X_Position = obj.scrollLeft;
var xPosition = obj.scrollLeft; // 更好,更簡潔
SCENE_GRAVITY = 1; // 常量
迴圈的性能 - 緩存數組長度
迴圈估計是Javascript性能調優最重要的部分了。在一個迴圈內部節省個一兩毫秒的,說不定總體就省出好幾秒來了。這裡有一招就是把數組的長度緩存,這樣在迴圈里就無需每次迭代的時候都計算一遍了。
var toLoop = new Array(1000);
for (var i = 0; i < toLoop.length; i++) {
// 敗家玩意 - 長度會反覆算 1000 次你知道不?
}
for (var i = 0, len = toLoop.length; i < len; i++) {
// 會過日子 - 長度只計算一次,然後緩存了
}
例外
如果你對一個數組做迴圈來查找並刪除某個元素,這就會改變數組長度。任何時候你只要會在迴圈內部增加或刪除元素來改變數組的長度,你就給自己帶來了麻煩。這種情況下,你要麼每次改變後重新設置數組長度,要麼就別緩存它了。
迴圈的性能 - 使用 'break;' 和 'continue;'
跳過和跳出迴圈的能力對於避免開銷很大的迴圈周期是非常有用的。
如果你是在迴圈內部查找,查找成功以後你會做什麼?比如1000個元素的迴圈執行到一半,你就找到了你要的東西。你不管三七二十一,即使知道後面的if語句不會再有符合的機會,還是讓迴圈繼續把剩下的500個元素迭代完麽?不!你應該跳出迴圈,必須的!
var bigArray = new Array(1000);
for (var i = 0, len = bigArray.length; i < len; i++) {
if (i === 500) {
break;
}
console.log(i); // 這樣只會輸出 0 - 499
}
另一個問題是跳過某個特定的迭代,然後繼續迴圈。雖然說類似於奇偶數這樣的條件可以通過把 i++ 替換成 i + 2 的辦法來管理,有些條件還是需要具體檢測,然後觸發跳過操作。任何能夠避免執行完整的迭代過程的東西都是很有用的。
var bigArray = new Array(1000);
for (var i = 0, len = bigArray.length; i < len; i++) {
if (condition) {
continue;
}
doCostlyStuff();
}
函數調用不要傳輸太多的參數
對於可讀性而不是其他因素來說,下麵這種方式真是糟透了:
function greet(name, language, age, gender, hairColour, eyeColour) {
alert(name);
}
面的例子預先構建了一個對象作為參數,或者把內聯對象傳遞過去,這樣就好多了。
function greet(user) {
alert(user.name);
}
greet({
name: "Bob",
gender: "male"
});
把 'this' 映射為 'self'
在編寫面向對象(OO)Javascript代碼的時候, 必須瞭解 this 的作用範圍. 不管你用來構建偽類的設計模式是什麼,對 this 的引用是指向一個實例的最簡單辦法。當你開始把jQuery的helper方法和你的偽類集成的時候,你就會註意到 this 的作用範圍變化。
Bob.findFriend("Barry");
Person.prototype.findFriend = function(toFind) {
// this = Bob
$(this.friends).each(function() {
// this = Bob.friends[i]
if (this.name === toFind) {
// this = Barry
return this;
}
});
}
在上面的例子里, this 經歷了從對 Bob 的引用,變成對他的朋友 Barry 的引用的過程。 瞭解 this 的取值在一段時間發生的變化是很重要的。在原型函數內部, this 指向所在偽類的當前實例(這裡是 Bob )。而一旦我們進入 $.each() 迴圈, this 就會重新映射為被解析數組的第 i 個元素。
解決辦法是把 this 的值重新映射為 self 或者 _self。雖然 self (不帶下劃線)並不是 保留字, 但它 確實是 window 對象的一個屬性。雖然我上面用到 self 的例子是從jQuery源代碼中挑的,但他們也認識到了這個錯誤,正打算 修正目前的狀況 ,也就是改用 **_self。我個人還是喜歡用 self** ,不為別的,就因為它的簡潔 -- 不過它可能會冒出一些非常令人困惑的bug。總之,用 self 有風險,使用需謹慎。
在下麵的例子中,我會更好地利用在 $.each() helper 中提供的參數,同時重新映射 this 的值。
Bob.findFriend("Barry");
Person.prototype.findFriend = function(toFind) {
// 就這一次用到了 "this"
var _self = this;
$(_self.friends).each(function(i,item) {
if (item.name === toFind) {
return item;
}
});
}
我能用 Boolean 嗎?
布爾變數必須能夠很容易通過命名來識別。可以用類似於 is, can 或者 has 的首碼來形成一個問句。
isEditing = true;
obj.canEdit = true;
user.hasPermission = true;
儘量減少重新繪製和重新佈局
重新繪製和重新佈局與重新渲染DOM的過程關聯,這個過程會在特定屬性或元素被改變時發生。重新繪製是在某個元素的外觀被改變但沒有對佈局進行調整的情況下觸發的。 Nicole Sullivan 在一篇全面的 博文 中把這些改變描述為諸如是否可見或背景色變化之類的樣式改變。重新佈局則是開銷更大的操作,由調整頁面佈局的一些改變引發。例如增加或刪除元素,改變某個元素的寬度或高度,甚至是改變瀏覽器視窗的大小。最糟糕的情況是重新佈局導致先輩、兄弟和孩子節點元素也需要重新佈局的多米諾骨牌效應。
毫無疑問,重新繪製和重新佈局應該儘量避免,但是如何做到呢?
重新佈局的例子
其實也不是說下麵的代碼就很糟糕啦。不過我們先假定數組 arr 有10個元素
var myList = document.getElementById("myList");
for (var i = 0, len = arr.length; i < len; i++) {
myList.innerHTML += "<li>" + arr[i].title + "</li>"; //重新佈局 -- 增加到元素
}
在上面的 for 迴圈里,每次迭代會觸發一次重新佈局。10次迭代就是10次重新佈局。
現在考慮下麵的代碼:
var constructedHTML = "";
for (var i = 0, len = arr.length; i < len; i++) {
constructedHTML += "<li>" + arr[i].title + "</li>"; //沒有重新佈局 - 增加到字元串
}
document.getElementById("myList").innerHTML = constructedHTML; //在這裡重新佈局
在這個場景里,需要增加的元素是在一個字元串里構建的。迴圈裡邊沒有產生任何重新佈局,因為DOM並沒有變化。只有當數組被完全迴圈完畢,構建的字元串被應用到某個對象的 innerHTML ,這才產生函數里唯一的一次重新佈局。
有無數種重新佈局和重新繪製是可以避免的,希望你幸運地瞭解了那些訣竅。這方面的閱讀材料汗牛充棟,不過大部分的材料都會引用到 Nicole Sullivan的這篇 博文 ,這是一個完美的起點。除了這裡的經驗,在涉及到"web 3.0"和HTML5時代的多種技術術語的時候,還有其他重要的經驗教訓值得汲取。上面的分析直接適用於寫jQuery代碼。在搗騰 canvas 的時候這些原則也很重要,另外儘量保持幀頻在30-60的範圍內。
不要用微秒來產生唯一的ID
自打web開發早期開始,就流行一種產生唯一ID的方法。具體做法是把從1970年1月1日開始計算的微秒數加到你的靜態ID後面,如下所示:
var myID = "static" + new Date().getTime();
這本來是相當萬無一失的方法,因為即便兩段這樣的代碼連續執行,在它們執行的間隙也會有幾毫秒。可是現在新的瀏覽器帶著新的Javascript引擎,伴隨著一直在提升的主頻。到現在,上面的代碼產生相同的毫秒數的可能性會比產生間隙的可能性更大。
這會導致傳統方法難以debug的bug。因為你的DOM是在運行中創建的,對頁面源代碼進行傳統的測試無法確定多個重覆ID的錯誤。Javascript和jQuery的錯誤處理機制會把第一個匹配的作為ID並忽略其他的重覆ID。所以它甚至都不會拋出JS錯誤!
這樣不行,唯一真正的方法是逐行設斷點和log,但是如果斷點的位置不對,你的毫秒數又不會衝突了!
好消息是有很多產生唯一ID的替代方法。學究氣一點的說法是,電腦的隨機數函數其實並不是真正隨機的,因為它還是來源於系統時間,雖然這一點值得註意,但是隨機數衝突的可能性是微乎其微的。
var myID = "static" + Math.round(Math.random() * 10000);
我個人更偏愛人工產生GUID方法。從技術角度說,GUID是根據你的硬體創建的,不過下麵的Javascript函數做得相當棒。這是我從 stack overflow 的一個帖子 里偷來的,相當順手的一個函數。
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
function guid() {
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}
var myID = "static" + guid();
檢測特性,而不是檢測瀏覽器類型
用戶的瀏覽器是否支持地理信息?用戶的瀏覽器是否支持web works?HTML5 視頻?HTML5 音頻?答案曾經是這樣的:
if ($.browser.msie) {
// 哦,是IE啊,那肯定不支持
}
但是世界在快速變化。最新版的IE幾乎能算是現代瀏覽器了,但它依舊給前端開髮帶來痛苦。更早版本的IE基本上和它之前的版本一樣爛,這就讓偷懶的Javascript程式員習慣於檢測 if (ie) 然後執行一些微軟專用的破語法。現在IE9已經廢棄了這些專用函數,那些原來的 if (ie) 老古董就反而會壞事了。
那麼,如果能檢測每個特性而不用檢測(既不可靠又能偽裝的)user-agent,你覺得咋樣?
如果你的回答是 "那相當靠譜", 那你就說對了。
用 Modernizr 吧,這是行業夢幻級大師Paul Irish參與開發的一個Javascript庫。該庫集廣泛應用、輕量級和海量文檔三大優勢於一身,實施無需動腦,實為居家旅行、殺人滅口必備。它會產生一個 Modernizr 對象,其中包含了它所有檢測測試的結果,這樣檢測某個特性的支持與否就和下麵的例子一樣簡單:
// 檢測瀏覽器是否支持canvas的老辦法
if (!!document.createElement('canvas').getContext) { ... }
// 用 Modernizr 檢測
if (Modernizr.canvas) { ... }
使用可讀的毫秒數
毫秒數的一種方便的寫法是寫成可讀的。對於初學者這很棒,但是大部分情況下其實只是一個噱頭。
// 這是3秒,30秒還是300秒啊?
var timeout = 30000;
// 增加了額外的計算開銷,但是讀和修改會更容易
var timeout = 30 * 1000;
關於jQuery代碼
像瘋狗一樣串接
jQuery最好的特性之一就是它的函數串接。你可能已經用過一點,也許把一些簡單的調用一個接一個串起來...但是你是否曾經像頭瘋狗一樣在DOM里上躥下跳地遍歷呢?還是花點時間來熟悉一下 .end() 函數。等你從起始選擇器開始在DOM里上躥下跳的時候,這個函數會很關鍵。
$(".quote")
.hide()
.find("a").text("Click here").bind("click",doStuff).end()
.parent().removeClass().addClass("testimonial").draggable().end()
.fadeIn("slow");
上例中,每次我們完成對某個DOM對象的操作,要反向遍歷DOM返回我們引用的原始對象的時候,就需要使用 .end() 函數。然後我們就順藤摸瓜扎回原來DOM里的位置了。
使用 data-* 屬性
你們當中那些已經寫了很長時間Javascript(原生的,不是jQuery)代碼的同學,很可能都熟悉各種屬性吧。你們想辦法設置它們,獲取它們,或者濫用 rel 和 title ...
別說HTML5 或者 jQuery 沒幫上忙哦。新的描述中允許在HTML元素中使用 data- 首碼來指明包含數據的屬性,jQuery會把指定的字元串轉換成正確的Javascript數據類型,這活乾的非常漂亮。我們來創建一個帶有某些數據屬性的 DIV 。
<div id="test" data-is-bool="true" data-some-number="123"></div>
現在,即使我們的值被包裝在引號裡面,它們也不會被當做字元串處理:
typeof $("#test").data("isBool"); // boolean
typeof $("#test").data("someNumber"); // number
特殊的大小寫
要註意,要讓這些代碼片段正常工作,(HTML里的屬性定義)必須使用小寫字母,這很重要。不過如果你是一位很強的前端開發者,你還是會想用駝峰法來命名你的數據變數。正如在Javascript里很多地方出現的,前置的連接符意味著下一個字母會適用於駝峰法大寫。不過,下麵的例子里在HTML屬性定義中使用駝峰法是 不行的 ,會讓上面的Javascript代碼返回 undefined。
不好使 :(
<div id="test" data-isBool="true" data-someNumber="123"></div>
好使 :)
<div id="test" data-is-bool="true" data-some-number="123"></div>
'.stop()' 停止協作和監聽
把jQuery動畫效果和滑鼠事件綁定是基於web的現代用戶交互方式中的關鍵部分,可是這方面即便某些最有名的網站也做得很蹩腳。這篇文章 提供了一個實現動畫的直接例子並且演示了這些動畫放在一起在視覺上會產生多麼不和諧的效果。 好在這個問題可以利用一個函數首碼或在 $.animate 調用中加入一個參數來輕鬆解決。
在使用 $.animate 的時候, 可以在參數中加入 queue: false 來避免串接。諸如 $.fadeIn 或 $.slideDown 這樣的動畫快捷方式不接受 queue 設置,你必須用 $.stop 這個方法預先停止這些動畫.。 在特定的場景下,需要某個動畫直接停下,或跳轉到變換的最終狀態。推薦你先熟悉有關 clearQueue 和 jumpToEnd 這兩個參數的相關 文檔 ,因為老天在上,我沒有其他辦法幫你。
$("selector").stop(true,true).fadeOut();
$("selector").animate({
property: value
}, {
duration: 1000,
queue: false
}
優化你的選擇器
jQuery 很高冷。它幾乎無所不能,不過它目前還沒法給你沖咖啡,我聽說在2.0版的路線圖裡有才這個特性。你需要當心的一件事是別濫用它的 sizzleJS 選擇器引擎的能力。想避免這種問題可以有兩個策略:緩存選擇器結果 以及 使用高效率的選擇器。
緩存選擇器結果
是每次你要修改一點東西的時候都先進行開銷巨大的DOM查詢,還是保存一份元素的索引?選擇一目瞭然。
// before
$(".quote a").bind("click", doStuff); // DOM查詢
// now
$(".quote a").addClass("quoteLink"); // DOM查詢!!
// later
$(".quote a").fadeIn("slow"); // 又一次DOM查詢!!!
忽略串接,這樣做更好:
// before
var $quoteLinks = $(".quote a"); // 只需一次DOM查詢
$quoteLinks.bind("click", doStuff);
// now
$quoteLinks.addClass("quoteLink");
// later
$quoteLinks.fadeIn("slow");
使用高效率的選擇器
好了,jQuery/sizzleJS 可以輕鬆使用CSS3選擇器,但是真正的開銷是什麼? 在這種場景下瀏覽器有可能會使用 document.querySelector(), 但是它也有可能分拆你的選擇器字元串,然後手工去查詢DOM。
// ID搜索是最快的查詢方式,然後它獲取孩子節點的列表,匹配其中class為'quotes'的元素
$("#quoteList").children(".quotes");
// 只在預先確定的bar元素下查找'foo'class
$(".foo",bar);
for' 迴圈總是比 'each()' 迴圈快
不管未來幾年在瀏覽器開發領域會發生什麼,本地的 for迴圈永遠會比jQuery的 $.each() 迴圈快。 當你思考jQuery到底是什麼(把本地JS函數包裝起來的一個庫)這種高大上問題的時候,你就會開始認識到本地原生Javascript代碼永遠會更快。用庫還是用原生,這是一個運行速度和編程速度之間的權衡。
很重要的一點是,對那些可能每秒調用數百次的性能關鍵的函數,總是要使用 for 迴圈。例如:
- 滑鼠移動
- 時間間隔
- 迴圈內部的迴圈
# CSS
## 理解盒子模型是關鍵
"盒子模型"對於理解瀏覽器如何渲染頁面是關鍵性決定性的因素。對其複雜性的全面理解有助於奇跡般地簡化你的工作。盒子模型描述了對HTML元素的物理維度進行計算的方式。如果一個塊元素具有固定寬度,比如說100px,那麼應該如何確定它的 padding, border 和 margin 呢?
很多網站都有深入的描述,但咱們簡單點說:在遵循標準的瀏覽器中,border和padding是被放在指定寬度之外的。最好是用圖形來解釋。比如下列代碼:
.foo {
width: 150px;
height: 150px;
padding: 25px;
border: 25px solid;
margin: 20px;
}
你可能估計的情況(Quirks 模式)
padding 和 border 都是往裡算的,結果保持高度和寬度都是150px。
你看到的情況(遵循標準模式)
可是,實際上出來的寬度和高度都是250px。 也就是150px + (2 * 25) + (2 * 25)。
如果你覺得這個結果很奇怪,那你不是一個人(呃,你是人,只是說還有其他人也會這麼想)。 現在手頭有個修複辦法,需要引入一個CSS屬性叫 box-sizing,這個屬性對於 IE8 及以上版本 都適用。它允許你選擇計算元素維度的確切方式,這樣就能救你於危難之中。具體支持的參數因瀏覽器而異,另外需要用到瀏覽器廠商的首碼,具體細節請參閱 caniuse.com 。
/* 舊方法 (178 + 20 + 2 = 200) */
.foo {
width: 178px;
padding: 10px;
border: 1px;
}
/* 更好的方法 */
.foo {
width: 200px;
padding: 10px;
border: 1px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
雖然說你也總是可以對寬度進行心算,在各個像素數中減來減去(就像第一個方法做的那樣),但在涉及到不同的寬度單位時(比如百分比或者EM),就沒人能搞清楚到底該怎麼做了。目前,除了把元素包在父元素中,以確保寬度和 padding/margin/borders 可以全部分開之外,也沒有別的解決辦法。
知道什麼時候用float,什麼時候用position
用table進行佈局的時代過去了。現在要承認我們可以集中精力去理解float和position的工作原理。這裡需要掌握一套特別的思維模型,我相信這件事最好是通過動手練習來進行。
用float從DOM中提取元素並強制它們靠到左邊或右邊,那是相當靠譜。它們已成為前端開發的後table佈局時代的萬金油,這可能是因為以前瀏覽器對於 display: inline 和 inline-block 的支持不力,還有對position的支持中冒出的 z-index bug。可現在就真的沒有藉口了。 inline-block 已經支持得很好了,簡單的一點修正就能讓它在 IE7 里應用。
謝天謝地,以前那些阻撓用CSS對元素進行絕對定位的爭論都消亡了。理論上,定位屬性可以讓你在頁面上以X和Y坐標放置元素,這種方式簡單直接,Flash開發者們都應該很熟悉。
理解 Position
用CSS定位元素的時候,理解一個事實非常重要:定位的位置總是相對於離它最近的有定位屬性的父元素而言的。人們剛開始用CSS的時候會有一個常見的誤解,認為 position: absolute; 是相對頁面的root元素定位的。 我覺得這種誤解來源於某些情況下,元素沒有任何父元素具備position樣式 -- 在這種情況下,他們的結論是對的。這樣向上遍歷DOM樹沒有找到任何有定位樣式的元素,就會定位到頁面的根元素上。
那麼,如果 position: absolute; 是把元素從他們所在的流中抽取出來,那你如何相對一個元素的父元素對它進行定位呢? 方法很直接。父元素需要定義 position: relative; 樣式,然後所有的孩子元素就會按上、右、下、左的順序依次擺放。利用這個知識,你會如何實現下麵很直觀的佈局呢?
使用 float,你會需要把這些元素包在一個父元素中, 然後把.one float靠左,然後改動 .two 和 .three 的 float 和 margin 。最後你應該寫出類似下麵的東西:
.parent {
/* ghetto clearfix */
width: 310px;
overflow: auto;
}
.one {
width: 200px;
height: 210px;
float: left;
}
.two {
width: 100px;
height: 100px;
float: right;
margin-bottom: 10px;
}
.three {
width: 100px;
height: 100px;
float: right;
}
正如我們前面所說,使用 position 讓我們可以用很明確的方式,按照 X 和 Y 坐標把元素顯示在屏幕上。 上面用float的方式會把頁面上的長文字隔開,下麵的方法則可以確保所有元素處於正常位置,無論頁面上有什麼內容。
.parent {
position: relative;
width: 310px;
height: 210px;
}
.one, .two, .three {
position: absolute;
}
.one {
top: 0;
left: 0;
width: 200px;
height: 210px;
}
.two {
top: 0;
right: 0;
width: 100px;
height: 100px;
}
.three {
bottom: 0;
right: 0;
width: 100px;
height: 100px;
}
如前文所述,有些 z-index 的問題需要考慮。雖然上面的例子可能顯得有點過分,不過一旦你開始思考定位,它會打開一個各種可能性的新世界.
留空
如果我們在單行和多行CSS參數的格式之間變來變去,CSS里的留空也會不一樣。我不打算對這個說太細。
合適的空白
/* 不好 */
.selector {display:none;background:#ff0000;color:#000000;}
/* 好 -- 單行 */
.selector { display: none; background: #ff0000; color: #000000; }
/* 好 -- 多行 */
.selector {
display: none;
background: #ff0000;
color: #000000;
}
大括弧不換行
.selector {
display: none;
background: #ff0000;
color: #000000;
}
子元素縮進
這個用不用就見仁見智了,我個人只會在單行定義的CSS文檔中用這種格式。
.selector { display: none; background: #ff0000; color: #000000; }
.selector a { text-decoration: none; }
.selector span { font-weight: bold; }
組合併縮進瀏覽器廠商首碼屬性
.selector {
background: #FFF; border: 1px solid #000; color: #EAEAEA;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
CSS 速記格式
屬性分組
把屬性分組到一起是大大減少CSS文件大小的最有效方法。這裡很重要的一點是要理解屬性是如何排序的(順時針 -- 上,右,下,左),以及如何進一步縮短它們(上和下,左和右)。
/* 逐個定義,太長了 */
padding-top: 1px;
padding-right: 2px;
padding-bottom: 1px;
padding-left: 2px;
/* 上,右,下,左,好很多 */
padding: 1px 2px 1px 2px;
/* 上和下,左和右,最優 */
padding: 1px 2px;
從 0px 到英雄
給值為 0 的屬性分配一個單位類型是多餘的。一個元素是距離左邊 0px 還是 0 elephants 根本不重要,只要知道它是貼著左邊就行了。
/* 不好 */
padding: 0px 10px;
/* 好 */
padding: 0 10px;
註釋塊
對於在一個樣式表裡維護多個樣式區域的任務,給大段CSS加上註釋是很好的辦法。顯然這和單行CSS風格配合使用效果更佳,不過這個效果在多行CSS風格裡也不是完全沒用。註釋里用破折號、等號還是下劃線起強調作用就見仁見智了,不過下麵是我喜歡的方式:
/* === HORIZONTAL NAV === */
#horizNav { width: 100%; display: block; }
#horizNav li { display: block; float: left; position: relative; }
#horizNav li a { display: block; height: 30px; text-decoration: none; }
#horizNav li ul { display: none; position: absolute; top: 30; left: 0; }
/* === HOME PAGE - CAROUSEL === */
#carousel { width: 960px; height: 150px; position: relative; }
#carousel img { display: none; }
#carousel .buttons { position: absolute; right: 10px; bottom: 10px; }
清除浮動
清除一個 <div>
過去意味著額外的DOM,因為這會涉及到增加一個額外的清除元素。更好的辦法是給父元素設置明確的寬度('auto'並不是在所有瀏覽器和場景中有效)以及把overflow屬性設為'auto'或者'hidden'。'hidden'顯然相容性更好,但在某些相容IE的版本里'auto'的效果好一些。
HTML:
<div class="parentElement">
<div class="childElement">
I'm floated left!
</div>
I'm normal text that wraps around the float
</div>
CSS:
.parentElement {
width: 100%;
overflow: hidden;
}
.childElement {
float: left;
}
有本項目的貢獻者提醒我註意最新的clearfix。 micro clear-fix 被認為相當穩定且跨瀏覽器相容,足以列入最新的HTML5 boilerplate發佈了。 我 強烈 建議你去看看。雖然我不是瀏覽器特定CSS和 :after 這種偽元素的狂熱粉絲,不過這個micro clearfix的確更健壯。它還能避免頂層margin縮回的問題。
垂直和水平居中
水平居中元素實際上不是什麼高精尖的科技,我敢肯定你們大部分人都熟悉下麵的代碼片段:
.class {
width: 960px;
margin: 0 auto;
}
前端開發者們使用這種代碼很長時間了,也沒搞明白為什麼這種方式對垂直居中不起作用。從我的理解說,很重要的一點是記住父元素一般會有個 height: auto; 樣式, 也沒有垂直居中元素所需的100%高度。而應用 position: absolute; 能有效地把元素轉移到定位模式,然後被設為auto的margin會自動幫助它調整位置,達到居中效果。
.exactMiddle {
width: 100px;
height: 100px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
這種方法的不足之處包括在 IE6 和 IE7 中缺乏支持,以及當瀏覽器被縮小到比居中對象還小時不出現滾動條。 在 這個網頁 里列出了更多的方法(現在這個是第4個),不過現在這個目前是最優方法。
在一個元素里垂直居中文字也是很直接的。如果文字是單行的,例如一個水平導航元素,你可以設置 line-height 為該元素的物理高度。
#horizNav li {
height: 32px;
line-height: 32px;
}
檢測特性,而不是檢測瀏覽器類型
在前面關於Javascript特性檢測的討論中,檢測到瀏覽器是 任何版本 的 IE 然後就運用某些屬性的做法越來越成問題了。鐵人 Paul Irish 引領了使用 IE 版本檢測 方法來解決這些問題的大潮,但是 Modernizr 從那時起拯救了我們。 Modernizr 在 root 元素里放入一些class,描述某些特性是否得到支持. 然後前沿的樣式就可以很容易從這些class級聯出來或者刪除掉。
.my_elem {
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.25);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,0.25);
box-shadow: 0 1px 2px rgba(0,0,0,0.25);
}
/* 如果 box shadow 不支持, 就應用 borders 屬性 */
.no-boxshadow .my_elem {
border: 1px solid #666;
border-bottom-width: 2px;
}
!important 不要亂用
依賴於 !important 標簽是個危險的現象。非用它不可的情況屈指可數,而且是特殊情況。這些情況大抵是需要覆蓋另外一套樣式表,而你沒法或者沒許可權編輯它。另一個場景是對元素的樣式硬編碼以防止Javascript線上產生的樣式有更大優先順序。而實際情況是 !important 往往被用做偷懶的快捷方式,讓某個樣式優先於其他的樣式,這樣做將來會產生很多問題。
對 !important 標簽的大部分使用是可以避免的,只要更好地理解CSS選擇器優先順序以及如何更準確地定位元素。選擇器越具體,被接受為適用樣式的可能性就越大。下麵來自 vanseodesign 的例子展示了具體化起作用的情況。
p { font-size: 12px; }
p.bio { font-size: 14px; }
關於樣式優先順序, 他們的文章 在解釋繼承性方面比我能寫出來的文章都好,所以請給它點個贊吧。
進取性向下相容
值得註意的是,這段是我的個人觀點,只適用於特定情況。在依賴老版本瀏覽器的大型商業項目或企業級解決方案中,進取性向下相容的立場將不容易被接受。
進取性向下相容的意思是如果某個特定的(老版本)瀏覽器無法渲染某個特定效果,則應直接忽略它。CSS3 按鈕就是一個好例子。諸如 border-radius, box-shadow, text-shadow 和 gradients 這些效果會在先進的瀏覽器里顯示出來。對於版本稍微老一點的瀏覽器,可以用一個 .PNG 圖片作為無傷大雅的補救辦法,而所有解決辦法中最優雅的應該是針對IE6提供一個PNG-Fix,或者用filter 參數來代替 gradients 和 shadows等屬性。 不過,在這種情況下,進取性向下相容方式會讓你忽略老版本瀏覽器,而在其中展示一個平面的還過得去的對象。
簡單地說,進取性向下相容說白了就是:如果你的瀏覽器渲染不了漸變色或盒子陰影,那是你運氣不好。
雖然這不是對所有情況都理想,這種方法能確保項目按時交付,且核心產品是可用的,而不需依賴於對瀏覽器的破解辦法。
CSS3及HTML5
這個話題我想我已經說的夠多了。用 Modernizr 來檢測特定的 HTML5 和 CSS3 特性是否可用。
@font-face的使用和濫用
在你考慮嵌入一套定製的字體之前,很重要的一點是你要查看 EULA 並檢查是否允許web嵌入。 字體庫廠商自然是不願意讓設計師和開發者有能力把字體庫文件直接存放在伺服器上,然後被熟練的終端用戶拷貝走。某些廠商也禁止嵌入特定的文件類型,例如 .TTF 和 .OTF。
如果,經過慎重考慮,你相信想要的字體是可嵌入的,那就去看一下Font Squirrel的 @font-face 生成器。 它利用了 Fontspring的 防彈 @font-face 結構 並能自動生成所有需要的文件格式。
向下相容
謝天謝地,瀏覽器對於它不支持的HTML5 和 CSS3 特性的處理已經達到了優雅的本色。加到 <input />
標簽的新類型例如 "email", "search" 等等在瀏覽器本地不支持的情況下一般會向下相容為正常的 <input type="text" />
。 類似的,不支持的CSS3 特性就不會出現,由高度和寬度媒體查詢控制的響應式佈局也不會被應用。
精巧的CSS3效果應該被應用為對使用現代瀏覽器的用戶的一種獎勵。
在下麵的"資源"小節里包括了一些有助於讓HTML5和CSS3功能在一批老版本瀏覽器中保持正常的庫。
資源
下列的資源對於代碼標準化和現代web頁面的互動是很重要的。它們確保了CSS3 和 HTML5 特性在以前缺乏支持能力的一批瀏覽器中能夠使用。
- jQuery JavaScript助手庫
- jQuery UI 為用戶交互/界面做著jQuery為JavaScript做的同樣的事
- Modernizr 檢測特性,不要檢測瀏覽器!
- RespondJS 為老版本瀏覽器帶來響應式佈局
- @font-face Generator 全面的字體內嵌
- RaphaelJS 簡單易用的跨瀏覽器向量圖
- HTML5 Boilerplate 對於任何項目都是一個好的起點。不過即使是它的"精簡"版也還是有點臃腫
- Twitter Bootstrap 讓你能給簡單的web應用迅速產生原型和樣式
---恢復內容結束---
>這篇文章很長,但的確是一篇非常乾的乾貨,講訴了 HTML、JavaScript、CSS、jQuery使用的一些規範與建議,前端的同學可以認真閱讀此文,並比較自己平時的一些習慣,看是否有改進的地方……
HTML
咋的啦,DOCTYPE?
不定義DOCTYPE是一種可以被判死刑的罪行。 以前你可能用的是下麵的DOCTYPE,不過你要知道現在已經有更簡潔清晰的代碼取而代之了。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
理想的狀況是用HTML5 DOCTYPE,所有現代的瀏覽器都支持它,即使是不支持HTML5的瀏覽器,例如IE6,IE7,也會由此轉入標準模式。 參見來源。
<!DOCTYPE html>
編寫合法且語義清晰的標記
用整潔、語義清晰的HTML編寫網站代碼是我們一直孜孜以求的。有時我們會發現前人配置頁面的方式限制了我們,或者有時我們編寫的是HTML格式的email模板。但永遠不要偏離HTML的規範,即使是為瞭解決特定瀏覽器相容性的bug。所有的標題應該從<h2>
開始分層級創建,文欄位落應該總是放在<p>
標簽里,諸如此類。如果你編寫的HTML的語義清晰,產生的頁面會更整潔、簡練,而且易於被搜索引擎爬蟲解析。這是你能做到的最簡單的SEO修補方式。來看看下麵的段落,你覺得哪個更整潔?是這個?
<span class="sectionHeading">A Heading</span>
<br /> <br />
Lorem ipsum dolor sit amet. ...
<br /> <br />
還是這個?
<h2>A Heading</h2>
<p>
Lorem ipsum dolor sit amet. ...
</p>
滑鼠中鍵點擊的應變方式
現代web應用最令人鬱悶的可用性缺陷之一是超鏈接功能的變種。 一些看起來像是超鏈接的元素可能是通過Javascript映射的單擊功能,這就破壞了滑鼠中鍵點擊(在新的tab中打開鏈接頁面)的功能。即使它們能在新的標簽頁打開,它們只帶有一個 # 的href又會把你帶回到同樣的頁面。深刻詮釋了該問題的一個現代熱門網站的例子就是Twitter。在它的整個應用里,滑鼠中鍵點擊用戶名或頭像會得到完全不同的結果。
<!-- 舊的方式,破壞網頁語義 -->
<a href="#"></a>
<!-- 如果滑鼠點擊不能產生一個頁面,那就不是超鏈接 -->
<span class="link" role="link"></span>
另一個替代方案是使用 # 引導的路徑,它會把普通的url映射為 # 引導的鏈接,然後通過AJAX來獲取頁面片段。提供此功能的庫應該能夠在滑鼠中鍵點擊的時候正常顯示頁面,或者在左鍵點擊時把該頁面內容載入到指定的區域。不過這樣也要慎重行事,有很多人都認為 鏈接正在破壞web應用
用Microformats格式表示聯繫人信息
Microformat是一種便於機器讀取聯繫人信息的方式。hCard類(不是vCard)用來定義元素里包含的內容類型。這些內容會被瀏覽器提取並突出顯示。
<span class="tel">
<span class="type">home</span>:
<span class="value">+1.415.555.1212</span>
</span>
如果你曾經瀏覽採用此格式的網頁,你會註意到類似skype的程式可以輕鬆檢測到網頁上的哪些數字是電話號碼。在iOS設備上的Safari瀏覽器也可以做到類似的事情。
有關Microformat的更多信息請參閱http://microformats.org/wiki/hcard
圖片需要設 'Alt' 文本
<img>
標簽需要 alt 文本,以便檢查並滿足可讀性的要求。 在 alt 屬性中的文本必須能夠說明圖片顯示的內容或要達到的效果,除非該圖片不重要。
如果圖片只是一個列表中的著重號或者其他無關緊要的圖標,最好是給 alt 屬性一個空字元串,但還是留著它。這樣屏幕閱讀器會忽略它,而不是把"著重號"連讀20次。
<img src="dog.gif" alt="Fido and I at the park!" />
<!-- 很好,描述清晰 -->
<img src="bullet.gif" alt="bullet" />
<!-- 不好,顯得多餘 -->
<img src="bullet.gif" alt="" />
<!-- 好 -->
只對錶格數據用table標簽
table標簽永遠只應該用在表格數據的展示上。唯一的例外是當編寫HTML格式的郵件時,這種情況下可能table是某些坑爹的郵件客戶端唯一支持的樣式了。
為了可讀性,表格頭永遠要使用 <th>
元素。同時切記要設置cellpadding, cellspacing 和 border 的值為 0 , 因為這些樣式由CSS來控制更容易確保一致性。
<table cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>
<th>
Cell Header
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Cell Item
</td>
</tr>
</tbody>
</table>
使用 jQuery 和 jQuery UI Widgets
jQuery 和 jQuery UI 被做成儘可能在不同瀏覽器上表現出幾乎相同的外觀和功能。 jQuery UI 被設計為符合 WAI WCAG 2.0 及 WAI ARIA, 因此採用該框架可以避免在你的站點上運行的插件或腳本的所有不確定性。
JavaScript代碼留空和格式
任何關於代碼格式、留空和大括弧位置的討論都會引起激烈辯論。對此,我想最簡單的規則就是,除非你願意把整個代碼文件重新格式化,不然還是尊重並保持已有代碼文件的格式。這意味著如果你看到一個JS文件里的大括弧沒有換行寫,那你的代碼也要繼續保持大括弧不換行。如果你的代碼沒有和代碼文件里的其他部分保持一致,那麼你的代碼就不應該通過代碼審查流程。
一致的代碼格式讓代碼更加易讀,同時也意味著代碼容易用查找/替換命令進行修改。謝天謝地,我們自己形成的編程習慣和jQuery正式推薦的方式非常相似。細微的差異也是有的,不過,那些是個人問題或者我們覺得沒法維護的一些東西。 參閱jQuery核心樣式指南
字元間距
// 不好
if(blah==="foo"){
foo("bar");
}
// 好 :)
if (blah === "foo") {
foo("bar");
}
大括弧不換行
// 不好
if (foo)
{
bar();
}
// 好 :)
if (foo) {
bar();
}
總是用大括弧
// 不好
if (foo)
bar();
// 好 :)
if (foo) {
bar();
}
字元串處理
引用字元串永遠要用雙引號。 有些人非常喜歡用C語言風格的字元串(單引號),但這種習慣會導致腳本內部的風格衝突。C語言風格的字元串處理要求空字元串和單字元包在單引號里,而短語和單詞必須包在雙引號內。
註釋
往代碼里玩命加註釋的需求是由各種經理、主管及其他很少接觸代碼的人們引領的。這種需求無非是雇員們考核指標中的一個勾選欄,花在這上面的時間基本沒有帶來什麼回報。 如果那些從善如流的開發者能遵循本文檔中提出的建議,他們的代碼會變得相當易於閱讀,一目瞭然,以至於再用註釋描述這些代碼會多餘得令人尷尬。來看下麵的例子。在這裡,布爾變數被作為問題提出,而函數也有直觀的命名。
if (user.hasPermission) {
editPage();
}
至少在這個場景中,註釋是完全沒有必要的。
註釋重要的情況
一個項目里,永遠會有某些部分難以查閱和理解。比如一個複雜的正則表達式,或者一個對角度進行計算或在度和弧度單位之間切換的數學函數。沒有上面的註釋,初級或中級的讀者將對腳本的含義毫無頭緒。
// 校驗美國電話號碼的正則表達式,號碼格式是 (XXX) XXX-XXXX (減號、空格和括弧都是可選的,可以有也可以沒有)
var phoneRegEx = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
總是使用 === 比較符
使用 == 比較符可以讓令人鬱悶的bug消失於無形。它允許在 JavaScript花園 中有清楚解釋的弱類型。使用嚴格的 === 比較符不會執行類型強制轉換,從而能夠嚴格地評估兩個對象之間的差別。再說一遍,更多詳細信息請參見 JavaScript花園。
var zeroAsAString = "0";
if (zeroAsAString == 0) {
// 這樣也能判斷為true,呵呵...
}
if (zeroAsAString === 0) {
// 判斷為false
}
例外
在和null進行比較的時候,允許使用 == 比較符,因為它會檢測null和undefined兩個屬性。如果你不完全理解這個原理,那我還是建議你用 === 比較符為好。
var foo = null;
// foo 是 null, 但 bar 是 undefined ,因為它尚未被聲明
if (foo == null && bar == null) {
// 上面的判斷還是成立的
}
使用 .parseInt() 的時候,總是指定第二個 'radix' 參數
把字元串解析為整數的時候,有個好習慣是指定第二個基數參數 -- 它會確定該字元串被轉換成幾進位。當字元串以 0 開頭的時候,預設情況下會觸發 16 進位作為基數。大部分初級和中級用戶只會用到 10 這個基數。 感謝 João Moreno 記錄的這個 勘誤。
alert( parseInt("08") ); // alerts: 2
alert( parseInt("08", 10) ); // alerts: 8
避免直接true 和 false
直接比較 true 和 false 的值是沒有必要的。有時候也許明確一下有好處,但它還是額外的代碼。
if (foo === true) {
// 用了 === 倒是不錯,可這是多餘的
}
if (foo) {
// 贊!
}
if (!bar) {
// 反過來也贊
}
避免污染全局命名空間
過分依賴全局變數是我們組所有人 -- 特別是我自己 -- 特別有負罪感的一件事。關於為啥全局變數不好的討論是相當直接的:這增加了腳本和變數衝突的概率,而且源文件和命名空間本身都會充斥著數不清的命名模糊的變數。
Douglas Crockford 堅信一個Javascript應用的代碼質量可以用其中使用的全局變數數來評價,越少越好。由於並不是什麼都可以定義成local的(不過要誠實,其實你現在考慮的那個是可以的,別偷懶),你需要想辦法整理你的變數以避免衝突,並把命名空間的膨脹減到最小。最簡單的方法就是採用單變數或者把用到這些全局變數的模塊儘可能減少。 Crockford提到YUI只用了一個全局變數,YAHOO。他在他的博文 "全局統治" 中討論了更多的細節問題。
考慮這種情況:對於小型web應用,全局變數通常用於保存應用級的設置,可以用你的項目名或者settings作為命名去定義一個對象,這樣總的來說會更好。
// 被污染的全局命名空間
var settingA = true;
var settingB = false;
var settingC = "test";
// 用 settings 作為對象命名
var settings = {
settingA: true,
settingB: false,
settingC: "test"
}
不過,如果我們可以通過避免使用全局變數來減少衝突概率,但是把命名空間標準化成一樣的,豈不