




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
編寫可維護的JavaScript目錄\h第一部分編程風格\h為什么要討論編程風格\h有用的工具\h第1章基本的格式化\h1.1縮進層級\h1.2語句結尾\h1.3行的長度\h1.4換行\(zhòng)h1.5空行\(zhòng)h1.6命名\h1.6.1變量和函數(shù)\h1.6.2常量\h1.6.3構造函數(shù)\h1.7直接量\h1.7.1字符串\h1.7.2數(shù)字\h1.7.3null\h1.7.4undefined\h1.7.5對象直接量\h1.7.6數(shù)組直接量\h第2章注釋\h2.1單行注釋\h2.2多行注釋\h2.3使用注釋\h2.3.1難于理解的代碼\h2.3.2可能被誤認為錯誤的代碼\h2.3.3瀏覽器特性hack\h2.4文檔注釋\h第3章語句和表達式\h3.1花括號的對齊方式\h3.2塊語句間隔\h3.3switch語句\h3.3.1縮進\h3.3.2case語句的“連續(xù)執(zhí)行”\h3.3.3default\h3.4with語句\h3.5for循環(huán)\h3.6for-in循環(huán)\h第4章變量、函數(shù)和運算符\h4.1變量聲明\h4.2函數(shù)聲明\h4.3函數(shù)調用間隔\h4.4立即調用的函數(shù)\h4.5嚴格模式\h4.6相等\h4.6.1eval()\h4.6.2原始包裝類型\h第二部分編程實踐\h第5章UI層的松耦合\h5.1什么是松耦合\h5.2將JavaScript從CSS中抽離\h5.3將CSS從JavaScript中抽離\h5.4將JavaScript從HTML中抽離\h5.5將HTML從JavaScript中抽離\h5.5.1方法1:從服務器加載\h5.5.2方法2:簡單客戶端模板\h5.5.3方法3:復雜客戶端模板\h第6章避免使用全局變量\h6.1全局變量帶來的問題\h6.1.1命名沖突\h6.1.2代碼的脆弱性\h6.1.3難以測試\h6.2意外的全局變量\h避免意外的全局變量\h6.3單全局變量方式\h6.3.1命名空間\h6.3.2模塊\h6.4零全局變量\h第7章事件處理\h7.1典型用法\h7.2規(guī)則1:隔離應用邏輯\h7.3規(guī)則2:不要分發(fā)事件對象\h第8章避免“空比較”\h8.1檢測原始值\h8.2檢測引用值\h8.2.1檢測函數(shù)\h8.2.2檢測數(shù)組\h8.3檢測屬性\h第9章將配置數(shù)據(jù)從代碼中分離出來\h9.1什么是配置數(shù)據(jù)\h9.2抽離配置數(shù)據(jù)\h9.3保存配置數(shù)據(jù)\h第10章拋出自定義錯誤\h10.1錯誤的本質\h10.2在JavaScript中拋出錯誤\h10.3拋出錯誤的好處\h10.4何時拋出錯誤\h10.5try-catch語句\h使用throw還是try-catch\h10.6錯誤類型\h第11章不是你的對象不要動\h11.1什么是你的\h11.2原則\h11.2.1不覆蓋方法\h11.2.2不新增方法\h11.2.3不刪除方法\h11.3更好的途徑\h11.3.1基于對象的繼承\(zhòng)h11.3.2基于類型的繼承\(zhòng)h11.3.3門面模式\h11.4關于Polyfill的注解\h11.5阻止修改\h第12章瀏覽器嗅探\h12.1User-Agent檢測\h12.2特性檢測\h12.3避免特性推斷\h12.4避免瀏覽器推斷\h12.5應當如何取舍\h第三部分自動化\h利弊\h第13章文件和目錄結構\h13.1最佳實踐\h13.2基本結構\h第14章Ant\h14.1安裝\h14.2配置文件\h14.3執(zhí)行構建\h14.4目標操作的依賴\h14.5屬性\h14.6Buildr項目\h第15章校驗\h15.1查找文件\h15.2任務\h15.3增強的目標操作\h15.4其他方面的改進\h15.5Buildr任務\h第16章文件合并和加工\h16.1任務\h16.2行尾結束符\h16.3文件頭和文件尾\h16.4加工文件\h第17章文件精簡和壓縮\h17.1文件精簡\h17.1.1使用YUICompressor精簡代碼\h17.1.2用ClosureCompiler精簡\h17.1.3使用UglifyJS精簡\h17.2壓縮\h17.2.1運行時壓縮\h17.2.2構建時壓縮\h第18章文檔化\h18.1JSDocToolkit\h18.2YUIDoc\h第19章自動化測試\h19.1YUITestSelenium引擎\h19.1.1配置一臺Selenium服務器\h19.1.2配置YUITestSelenium引擎\h19.1.3使用YUITestSelenium引擎\h19.1.4Ant的配置寫法\h19.2Yeti\h19.3PhantomJS\h19.3.1安裝及使用\h19.3.2Ant的配置寫法\h19.4JsTestDriver\h19.4.1安裝及使用\h19.4.2Ant的配置寫法\h第20章組裝到一起\h20.1被忽略的細節(jié)\h20.2編制打包計劃\h20.2.1開發(fā)版本的構建\h20.2.2集成版本的構建\h20.2.3發(fā)布版本的構建\h20.3使用CI系統(tǒng)\h20.3.1Jenkins\h20.3.2其他CI系統(tǒng)\h附錄AJavaScript編碼風格指南\hA.1縮進\hA.2行的長度\hA.3原始值\hA.4運算符間距\hA.5括號間距\hA.6對象直接量\hA.7注釋\hA.7.1單行注釋\hA.7.2多行注釋\hA.7.3注釋聲明\hA.7.4變量聲明\hA.7.5函數(shù)聲明\hA.7.6命名\hA.7.7嚴格模式\hA.7.8賦值\hA.7.9等號運算符\hA.7.10三元操作符\hA.7.11語句\hA.7.12留白\hA.7.13需要避免的\h附錄BJavaScript工具集\hB.1構建工具\hB.2文檔生成器\hB.3代碼檢查工具\hB.4壓縮工具\hB.5測試工具第一部分編程風格“程序是寫給人讀的,只是偶爾讓計算機執(zhí)行一下?!薄狣onaldKnuth。\h[1]當你剛剛組建一個團隊時,團隊中的每個人都各自有一套編程習慣。畢竟,每個成員都有著不同的背景。有些人可能來自某個“皮包公司”(one-manshop),身兼數(shù)職,在公司里什么事都做;還有些人會來自不同的團隊,對某種特定的做事風格情有獨鐘(或恨之入骨)。每個人都覺得代碼應當按照自己的想法來寫,這些通常被歸納為個人編程嗜好。在這個過程中\(zhòng)h[2]應當盡早將確定統(tǒng)一的編程風格納入議題。我們會經(jīng)常碰到這兩個術語:“編程風格”(styleguideline)和“編碼規(guī)范”(codeconvention)。編程風格是編碼規(guī)范的一種,用來規(guī)約單文件中代碼的規(guī)劃。編碼規(guī)范還包含編程最佳實踐、文件和目錄的規(guī)劃以及注釋等方面。本書集中討論JavaScript的編碼規(guī)范。為什么要討論編程風格提煉編程風格是一道工序,花再多的時間也不為過。畢竟每個人都有自己的想法,如果一天當中你有8小時是在寫代碼,那么你自然希望用一種舒服的方式來寫代碼。剛開始,團隊成員對新的編程風格有點不適應,全靠強勢的項目組長強制推行才得以持續(xù)。一旦風格確立后,這套編程風格就會促成團隊成員高水準的協(xié)作,因為所有代碼(的風格)看起來極為類似。在團隊開發(fā)中,所有的代碼看起來風格一致是極其重要的,原因有以下幾點。任何開發(fā)者都不會在乎某個文件的作者是誰,也沒有必要花費額外精力去理解代碼邏輯并重新排版,因為所有代碼排版格式看起來非常一致。我們打開一個文件時所干的第一件事,常常不是立即開始工作而是首先修復代碼的縮進,當項目很龐大時,你會體會到統(tǒng)一的編程風格的確大幅度節(jié)省了時間成本。我能很容易地識別出問題代碼并發(fā)現(xiàn)錯誤。如果所有代碼看起來很像,當你看到一段與眾不同的代碼時,很可能錯誤就產(chǎn)生在這段代碼中。毫無疑問,全球性的大公司都對外或對內發(fā)布過編程風格文檔。編程風格是個人的事情,只有放到團隊開發(fā)中才能發(fā)揮作用。本書的這部分給出了JavaScript編碼規(guī)范中值得關注(推薦)的方面。在某些場景中,很難說哪種編程風格好,哪種編程風格不好,因為有些編程風格只是某些人的偏好。本章不是向你灌輸我個人的風格偏好,而是提煉出了編程風格應當遵循的重要的通用準則。本書附錄A中給出了我個人的JavaScript編程風格。有用的工具開發(fā)編碼指南是一件非常困難的事情——執(zhí)行是另外一回事。在團隊中通過討論達成一致和進行代碼評審(codereview)時,每個人都很關注編碼風格,但在平時大家卻常常將這些拋在腦后。工具可以對每個人實時跟蹤。這里有兩個用來檢查編程風格的工具,這兩個工具非常有用:JSLint和JSHint。JSLint是由DouglasCrockford創(chuàng)建的。這是一個通用的JavaScript代碼質量檢查工具。最開始,JSLint只是一個簡單的查找不符合JavaScript模式的、錯誤的小工具。經(jīng)過數(shù)年的進化,JSLint已經(jīng)成為一個有用的工具,不僅僅可以找出代碼中潛在的錯誤,而且能針對你的代碼給出編碼風格上的警告。Crockford將他對JavaScript風格的觀點分成了三個不同的部分。“JavaScript風格的組成部分(第一部分)”(\h:///style1.html),包含基本的模式和語法?!癑avaScript風格的組成部分(第二部分)”(\h:///style2.html),包含一般性的JavaScript慣用法。“JavaScript編程語言的編碼規(guī)范”(:///code.html),這個規(guī)范更加全面,從前兩部分中提煉出了編程風格的精華部分,同時增補了少量的編程風格指引。JSLint直接吸納了很多Crockford所提煉的編程風格,而且很多時候我們無法關閉JSLint中檢查編程風格的功能。所以JSLint是一個非常棒的工具,當然前提是你認可Crockford關于編程風格的觀點。JSHint是JSLint的一個分支項目,由AntonKovalyov創(chuàng)建并維護。JSHint的目標是提供更加個性化的JavaScript代碼質量和編程風格檢查的工具。比如,當出現(xiàn)語法錯誤的時候,JSHint幾乎可以關掉所有編程風格檢查,這樣你可以完全自定義消息提示。Kovalyov非常鼓勵大家通過GitHub(\h://)上的源代碼庫參與JSHint項目并為之貢獻代碼。你可以將這些工具中的一種集成到打包過程中,通過這種方式推行編碼規(guī)范是一個不錯的方法。這種方法同時可以監(jiān)控你的JavaScript代碼中潛在的錯誤。\h[1]譯注:高德納(DonaldErvinKnuth)是世界頂級計算機科學家之一,被公認為現(xiàn)代計算機科學的鼻祖,著有《計算機程序設計藝術》(TheArtofComputerProgramming)等經(jīng)典著作,在不多的業(yè)余時間里,Knuth不僅寫小說,還是一位音樂家、作曲家、管風琴設計師。\h[2]譯注:意指組建團隊的過程。
第1章基本的格式化編程風格指南的核心是基本的格式化規(guī)則(formattingrule)。這些規(guī)則直接決定了如何編寫高水準的代碼。與在學校學習寫字時所用的方格紙類似,基本的格式化規(guī)則將指引開發(fā)者以特定的風格編寫代碼。這些規(guī)則通常包含一些你不太在意的有關語法的信息,但對于編寫清晰連貫的代碼段來說,每一條信息都是非常重要的。1.1縮進層級關于JavaScript編碼風格,我們首先要討論的是(幾乎所有的語言都是如此)如何處理縮進。對這個話題是可以爭論上好幾個小時的,縮進甚至關系到軟件工程師的價值觀。在確定編程風格之初應當首先確定縮進格式,這非常重要,以免工程師后續(xù)會陷入那個老生常談的打開文件時二話不說先重排代碼縮進的問題之中。來看一下這段代碼(為了演示,這里故意修改了示例代碼的縮進)。if(wl&&wl.length){
for(i=0,l=wl.length;i<l;++i){
p=wl[i];
type=Y.Lang.type(r[p]);
if(s.hasOwnProperty(p)){if(merge&&type=='object'){
Y.mix(r[p],s[p]);
}elseif(ov||!(pinr)){
r[p]=s[p];
}
}
}
}快速讀懂這段代碼并不容易。這里的縮進并不統(tǒng)一,一眼看去else是對應到第1行的if語句。但實際上這個else和代碼第5行的if語句相對應。罪魁禍首是多位開發(fā)人員在同一段代碼里應用了不同的縮進風格。這恰恰說明了統(tǒng)一縮進風格的重要性。如果有適當?shù)目s進,這段代碼將變得更加易讀。if(wl&&wl.length){
for(i=0,l=wl.length;i<l;++i){
p=wl[i];
type=Y.Lang.type(r[p]);
if(s.hasOwnProperty(p)){
if(merge&&type=='object'){
Y.mix(r[p],s[p]);
}elseif(ov||!(pinr)){
r[p]=s[p];
}
}
}
}堅持使用適度的縮進是萬里長征的第一步——本章在下面將提到這種做法可以帶來其他可維護性方面的提升。對于大多數(shù)編程風格來說,代碼到底應該如何縮進并沒有統(tǒng)一的共識。有兩種主張。使用制表符進行縮進每一個縮進層級都用單獨的制表符(tabcharacter)表示。所以一個縮進層級是一個制表符,兩個縮進層級為兩個制表符,以此類推。這種方法有兩個主要的好處。第一,制表符和縮進層級之間是一對一的關系,這是符合邏輯的。第二,文本編輯器可以配置制表符的展現(xiàn)長度\h[1],因此那些想修改縮進尺寸的開發(fā)者可以通過配置文本編輯器來實現(xiàn),想長即長,想短可短。使用制表符作縮進的主要缺點是,系統(tǒng)對制表符的解釋不一致。你會發(fā)覺在某個系統(tǒng)中用一款編輯器打開文件時看到的縮進,和在另外一個系統(tǒng)中用相同的編輯器打開文件時看到的不一樣。對于那些追求(代碼展現(xiàn))一致性的開發(fā)者來說,這會帶來一些困惑。這些差異、爭論會導致不同的開發(fā)者對同一段代碼有不同的看法的,而這些正是團隊開發(fā)需要規(guī)避的。使用空格符進行縮進每個縮進層級由多個空格字符組成。在這種觀點中有三種具體的做法:2個空格表示一個縮進,4個空格表示一個縮進,以及8個空格表示一個縮進。這三種做法在其他很多編程語言中都能找到淵源。實際上,很多團隊選擇4個空格的縮進,對于那些習慣用2個空格縮進和用8個空格縮進的人來說,4個空格縮進是一種折中的選擇。使用空格作縮進的好處是,在所有的系統(tǒng)和編輯器中,文件的展現(xiàn)格式不會有任何差異??梢栽谖谋揪庉嬈髦信渲们脫鬞ab鍵時插入幾個空格。也就是說所有開發(fā)者都可以看到一模一樣的代碼呈現(xiàn)。使用空格縮進的缺點是,對于單個開發(fā)者來說,使用一個沒有配置好的文本編輯器創(chuàng)建格式化的代碼的方式非常原始。盡管有人爭辯說應當優(yōu)先考慮使用一種縮進約定,但說到底這只是一個團隊偏好的問題。這里我們給出一些各式各樣的縮進風格作為參考。jQuery核心風格指南(jQueryCoreStyleGuide)明確規(guī)定使用制表符縮進。DauglasCrockford的JavaScript代碼規(guī)范(DouglasCrockford’sCodeConventionsfortheJavaScriptProgrammingLanguage)規(guī)定使用4個空格字符的縮進。SproutCore風格指南(SproutCoreStyleGuide)規(guī)定使用2個空格的縮進。Goolge的JavaScript風格指南(GoogleJavaScriptStyleGuide)規(guī)定使用2個空格的縮進。Dojo編程風格指南(DojoStyleGuide)規(guī)定使用制表符縮進。我推薦使用4個空格字符為一個縮進層級。很多文本編輯器都默認將縮進設置為4個空格,你可以在編輯器中配置敲入Tab鍵時插入4個空格。使用2個空格的縮進時,代碼的視覺展現(xiàn)并不是最優(yōu)的,至少看起來是這樣。盡管是選擇制表符還是選擇空格做縮進只是一種個人偏好,但絕對不要將兩者混用,這非常重要。這么做會導致文件的格式很糟糕,而且需要做不少清理工作,就像本節(jié)的第一段示例代碼顯示的那樣。1.2語句結尾有一件很有意思且很容易讓人困惑的事情,那就是JavaScript的語句要么獨占一行,要么以分號結尾。類似C的編程語言,諸如C++和Java,都采用這種行結束寫法,即結尾使用分號。下面這兩段示例代碼都是合法的JavaScript。//合法的代碼
varname="Nicholas";
functionsayName(){
alert(name);
}
//合法的代碼,但不推薦這樣寫
varname="Nicholas"
functionsayName(){
alert(name)
}有賴于分析器的自動分號插入(AutomaticSemicolonInsertion,ASI)機制,JavaScript代碼省略分號也是可以正常工作的。ASI會自動尋找代碼中應當使用分號但實際沒有分號的位置,并插入分號。大多數(shù)場景下ASI都會正確插入分號,不會產(chǎn)生錯誤。但ASI的分號插入規(guī)則非常復雜且很難記住,因此我推薦不要省略分號??匆幌逻@段代碼。//原始代碼
functiongetData(){
return
{
title:"MaintainableJavaScript",
author:"NicholasC.Zakas"
}
}
//分析器會將它理解成
functiongetData(){
return;
{
title:"MaintainableJavaScript",
author:"NicholasC.Zakas"
};
}在這段代碼中,函數(shù)getData()的本意是返回一個包含一些數(shù)據(jù)的對象。然而,return之后新起了一行,導致return后被插入了一個分號,這會導致函數(shù)返回值是undefined??梢酝ㄟ^將左花括號移至與return同一行的位置來修復這個問題。//這段代碼工作正常,盡管沒有用分號
functiongetData(){
return{
title:"MaintainableJavaScript",
author:"NicholasC.Zakas"
}
}ASI在某些場景下是很管用的,特別是,有時候ASI可以幫助減少代碼錯誤。當某個場景我們認為不需要插入分號而ASI認為需要插入時,常常會產(chǎn)生錯誤。我發(fā)現(xiàn)很多開發(fā)人員,尤其是新手們,更傾向于使用分號而不是省略它們。DouglasCrockford針對JavaScript提煉出的編程規(guī)范(下文統(tǒng)稱為Crockford的編程規(guī)范)推薦總是使用分號,同樣,jQuery核心風格指南、Google的JavaScript風格指南以及Dojo編程風格指南都推薦不要省略分號。如果省略了分號,JSLint和JSHint默認都會有警告。1.3行的長度和縮進話題息息相關的是行的長度。如果一行代碼太長,編輯窗口出現(xiàn)了橫向滾動條,會讓開發(fā)人員感覺很別扭。即便是在當今的寬屏顯示器中,保持合適的代碼行長度也會極大地提高工程師的生產(chǎn)力。很多語言的編程規(guī)范都提到一行代碼最長不應當超過80個字符。這個數(shù)值來源于很久之前文本編輯器的單行最多字符限制,即編輯器中單行最多只能顯示80個字符,超過80個字符的行要么折行,要么被隱藏起來,這些都是我們所不希望的。相比20年前的編輯器,現(xiàn)在的文本編輯器更加精巧,但仍然有很多編輯器保留了單行80個字符的限制。此外關于行長度,還有一些常見的建議。1.Java語言編程規(guī)范中規(guī)定源碼里單行長度不超過80個字符,文檔中代碼單行長度不超過70個字符。2.Android開發(fā)者編碼風格指南規(guī)定單行代碼長度不超過100個字符。3.非官方的Ruby編程規(guī)范中規(guī)定單行代碼長度不超過80個字符。4.Python編程規(guī)范中規(guī)定單行代碼長度不超過79個字符。JavaScript風格指南中很少提及行的長度,但Crockford的代碼規(guī)范中指定一行的長度為80個字符。我也傾向于將行長度限定在80個字符。1.4換行當一行長度達到了單行最大字符數(shù)限制時,就需要手動將一行拆成兩行。通常我們會在運算符后換行,下一行會增加兩個層級的縮進。比如(假定縮進為4個字符)下面這樣。//好的做法:在運算符后換行,第二行追加兩個縮進
callAFunction(document,element,window,"somestringvalue",true,123,
navigator);
//不好的做法:第二行只有一個縮進
callAFunction(document,element,window,"somestringvalue",true,123,
navigator);
//不好的做法:在運算符之前換行了
callAFunction(document,element,window,"somestringvalue",true,123
,navigator);在這個例子中,逗號是一個運算符,應當作為前一行的行尾。這個換行位置非常重要,因為ASI機制會在某些場景下在行結束的位置插入分號??偸菍⒁粋€運算符置于行尾,ASI就不會自作主張地插入分號,也就避免了錯誤的發(fā)生。對于語句來說,同樣也可以應用下面這種換行規(guī)則。if(isLeapYear&&isFebruary&&day==29&&itsYourBirthday&&
noPlans){
waitAnotherFourYears();
}在這段代碼中,if條件語句被拆分成了兩行,斷行在&&運算符之后。需要注意的是,if語句的主體部分依然是一個縮進,這樣更容易閱讀。這個規(guī)則有一個例外:當給變量賦值時,第二行的位置應當和賦值運算符的位置保持對齊。比如:varresult=something+anotherThing+yetAnotherThing+somethingElse+
anotherSomethingElse;這段代碼里,變量anotherSomethingElse和首行的something保持左對齊,確保代碼的可讀性,并能一眼看清楚折行文本的上下文。1.5空行在編程規(guī)范中,空行是常常被忽略的一個方面。通常來講,代碼看起來應當像一系列可讀的段落,而不是一大段揉在一起的連續(xù)文本。有時一段代碼的語義和另一段代碼不相關,這時就應該使用空行將它們分隔,確保語義有關聯(lián)的代碼展現(xiàn)在一起。我們可以為1.1節(jié)里的示例代碼加入一些空行,以更好地提升代碼的可讀性,下面是最初的代碼:if(wl&&wl.length){
for(i=0,l=wl.length;i<l;++i){
p=wl[i];
type=Y.Lang.type(r[p]);
if(s.hasOwnProperty(p)){
if(merge&&type=='object'){
Y.mix(r[p],s[p]);
}elseif(ov||!(pinr)){
r[p]=s[p];
}
}
}
}給這段代碼添加了幾個空行之后,得到:if(wl&&wl.length){
for(i=0,l=wl.length;i<l;++i){
p=wl[i];
type=Y.Lang.type(r[p]);
if(s.hasOwnProperty(p)){
if(merge&&type=='object'){
Y.mix(r[p],s[p]);
}elseif(ov||!(pinr)){
r[p]=s[p];
}
}
}
}這段示例代碼中所展示的編程規(guī)范是在每個流控制語句之前(比如if和for語句)添加空行。這樣做能使你更流暢地閱讀這些語句。一般來講,在下面這些場景中添加空行也是不錯的主意。在方法之間。在方法中的局部變量(localvariable)和第一條語句之間。在多行或單行注釋之前。在方法內的邏輯片段之間插入空行,提高可讀性。但并沒有一個編程規(guī)范對空行的使用給出任何具體建議,Crockford的編程規(guī)范也只提到要審慎地使用空行。1.6命名“計算機科學只存在兩個難題:緩存失效和命名。”——PhilKarlton。只要是寫代碼,都會涉及變量和函數(shù),因此變量和函數(shù)命名對于增強代碼可讀性至關重要。JavaScript語言的核心ECMAScript,即是遵照了駝峰式大小寫(Camelcase)\h[2]命名法。駝峰式大小寫(CamelCase)命名法是由小寫字母開始的,后續(xù)每個單詞首字母都大寫,比如:varthisIsMyName;
varanotherVariable;
varaVeryLongVariableName;一般來講,你應當遵循你使用的語言核心所采用的命名規(guī)范,因此大部分JavaScript程序員使用駝峰命名法來給變量和函數(shù)命名。Google的JavaScript風格指南、SproutCore編程風格指南以及Dojo編程風格指南在大部分場景中也都采用了小駝峰(CamelCase)命名。盡管小駝峰(CamelCase)命名法是最常見的命名方法,但我們不排斥更多其他的命名風格。在2000年左右,JavaScript中流行另外一種命名方法——匈牙利命名法。這種命名方法的特點是,名字之前冠以類型標識符前綴,比如sName表示字符串,iCount表示整數(shù)。這種風格已經(jīng)是明日黃花風光不再了,當前主流的編程規(guī)范都不推薦這種命名法。1.6.1變量和函數(shù)變量名應當總是遵守駝峰大小寫命名法,并且命名前綴應當是名詞。以名詞作為前綴可以讓變量和函數(shù)區(qū)分開來,因為函數(shù)名前綴應當是動詞。這里有一些例子。//好的寫法
varcount=10;
varmyName="Nicholas";
varfound=true;
//不好的寫法:變量看起來像函數(shù)
vargetCount=10;
varisFound=true;
//好的寫法
functiongetName(){
returnmyName;
}
//不好的寫法:函數(shù)看起來像變量
functiontheName(){
returnmyName;
}命名不僅是一門科學,更是一門技術,但通常來講,命名長度應該盡可能短,并抓住要點。盡量在變量名中體現(xiàn)出值的數(shù)據(jù)類型。比如,命名count、length和size表明數(shù)據(jù)類型是數(shù)字,而命名name、title、和message表明數(shù)據(jù)類型是字符串。但用單個字符命名的變量諸如i、j、和k通常在循環(huán)中使用。使用這些能夠體現(xiàn)出數(shù)據(jù)類型的命名,可以讓你的代碼容易被別人和自己讀懂。要避免使用沒有意義的命名。那些諸如foo、bar和tmp之類的命名也應當避免,當然開發(fā)者的工具箱中還有很多這樣可以隨拿隨用的名字,但不要讓這些命名承載其他的附加含義。對于其他開發(fā)者來說,如果沒有看過上下文,是無法理解這些變量的用處的。對于函數(shù)和方法命名來說,第一個單詞應該是動詞,這里有一些使用動詞常見的約定。
動詞含義can函數(shù)返回一個布爾值has函數(shù)返回一個布爾值is函數(shù)返回一個布爾值get函數(shù)返回一個非布爾值set函數(shù)用來保存一個值以這些約定作為切入點可以讓代碼可讀性更佳,這里有一些例子。if(isEnabled()){
setName("Nicholas");
}
if(getName()==="Nicholas"){
doSomething();
}盡管這些函數(shù)命名細則并沒有被歸納入當下流行的編程風格中,但在很多流行的庫中,JavaScript開發(fā)者會發(fā)現(xiàn)存在不少這種“偽標準”(pseudostandard)。jQuery顯然并沒有遵循這種函數(shù)命名約定,一部分原因在于jQuery中方法的使用方式,很多方法同時用作getter和setter。比如,$("body").attr("class")可以取到class屬性的值,而$("body").attr("class","selected")可以給class屬性賦值。盡管如此,我還是推薦使用動詞作為函數(shù)名前綴。1.6.2常量在ECMAScript6之前,JavaScript中并沒有真正的常量的概念。然而,這并不能阻止開發(fā)者將變量用作常量。為了區(qū)分普通的變量(變量的值是可變的)和常量(常量的值初始化之后就不能變了),一種通用的命名約定應運而生。這個約定源自于C語言,它使用大寫字母和下劃線來命名,下劃線用以分隔單詞,比如:varMAX_COUNT=10;
varURL=":///";需要注意的是,這里僅僅是應用了不同命名約定的變量而已,因此它們的值都是可以被修改的。使用這種不同的約定來定義普通的變量和常量,使兩者非常易于區(qū)分。來看一下這段代碼:if(count<MAX_COUNT){
doSomething();
}在這段代碼中,一眼就能看出count是變量,其值是可變的,而MAX_COUNT表示常量,它的值不會被修改。這個約定為底層代碼(underlyingcode)增加了另外一層語義。Google的JavaScript風格指南、SproutCore編程風格指南以及Dojo編程風格指南中都提到,要使用這種習慣來命名常量。(Dojo編程風格指南中也允許使用大駝峰命名法大小寫(PascalCase)來命名常量,接下來會提到)。1.6.3構造函數(shù)在JavaScript中,構造函數(shù)只不過是前面冠以new運算符的函數(shù),用來創(chuàng)建對象。語言本身已經(jīng)包含了很多內置構造函數(shù),比如Object和RegExp,同樣開發(fā)者也可以創(chuàng)建自己的構造函數(shù)來生成新類型。正如其他的命名約定一樣,構造函數(shù)的命名風格也和本地語言(NativeLanguage)保持一致,因此構造函數(shù)的命名遵照大駝峰命名法(PascalCase)。PascalCase和CamelCase都表示“駝峰大小寫”,二者的區(qū)別在于PascalCase以大寫字母開始。因此anotherName可以替換成AnotherName。這樣做可以將構造函數(shù)從變量和普通函數(shù)中區(qū)分出來。構造函數(shù)的命名也常常是名詞,因為它們是用來創(chuàng)建某個類型的實例的。這里有一些例子://好的做法
functionPerson(name){
=name;
}
Ptotype.sayName=function(){
alert();
};
varme=newPerson("Nicholas");遵守這條約定同樣可以幫助我們快速定位問題,這接下來會提到。你知道在以大駝峰命名法(Pascalcase)命名的函數(shù)如果是名詞的話,前面一定會有new運算符。看一下這段代碼:varme=Person("Nicholas");
varyou=getPerson("Michael");這段代碼中,根據(jù)上文提到的命名約定,我們一眼就可以看出第一行出了問題,但第二行看起來還ok。Crockford的編程規(guī)范、Google的JavaScript風格指南以及Dojo編程風格指南都推薦這種實踐。如果構造函數(shù)的首字母不是大寫,或者構造函數(shù)之前沒有new運算符,JSLint都會給出警告。而在JSHint中,只有你開啟了一個特殊的newcap選項后,才會對首字母不是大寫的構造函數(shù)給出警告。1.7直接量JavaScript中包含一些類型的原始值:字符串、數(shù)字、布爾值、null和undefined。同樣也包含對象直接量和數(shù)組直接量。這其中,只有布爾值是自解釋(self-explanatory)的,其他的類型或多或少都需要思考一下它們如何才能更精確地表示出來。1.7.1字符串在JavaScript中,字符串是獨一無二的。字符串可以用雙引號括起來,也可以用單引號括起來。比如://合法的JavaScript代碼
varname="Nicholassays,\"Hi.\"";
//也是合法的JavaScript代碼
varname='Nicholassays,"Hi"';和Java、PHP這些語言不同,使用單引號括起字符串和雙引號括起字符串在功能上并無不同。除了內部出現(xiàn)字符串界定符(stringdelimiter)時需要轉義之外,兩種做法在功效上完全一致。因此在這段示例代碼中,在使用雙引號括起來的字符串里需要對雙引號進行轉義,而在使用單引號括起來的字符串里則不必如此。你需要關心的是,你的代碼應當從頭到尾只保持一種風格。Crockford的編程規(guī)范和jQuery核心風格指南都使用雙引號來括住字符串。Google的JavaScript風格指南使用單引號括住字符串。我傾向于使用雙引號,因為我經(jīng)常在Java和JavaScript之間來回切換。由于Java只使用雙引號括住字符串,我發(fā)現(xiàn)如果在JavaScript中也使用這個約定,我會很容易在上下文之間相互切換。這類問題應當在制定規(guī)范之初就考慮清楚:這樣可以最大程度地減輕工程師的開發(fā)負擔。關于字符串還有另外一個問題需要注意,即創(chuàng)建多行字符串。這個特性并非來自JavaScript語言本身,卻在幾乎所有的(JavaScript)引擎中正常工作。//不好的寫法
varlongString="Here'sthestory,ofaman\
namedBrady.";盡管從技術上講這種寫法是非法的JavaScript語法,但的確能在代碼中創(chuàng)建多行字符串。通常不推薦使用這種寫法,因為它是一種奇技淫巧而非語言特性,并且在Google的JavaScript風格指南中是明確禁止的。多行字符串的一種替代寫法是,使用字符串連接符(+)將字符串分成多份。//Good
varlongString="Here'sthestory,ofaman"+
"namedBrady.";1.7.2數(shù)字在JavaScript中的數(shù)字類型只有一種,因為所有數(shù)字形式——整數(shù)和浮點數(shù)——都存儲為相同的數(shù)據(jù)類型。還有一些其他的數(shù)字直接量格式來表示不同的數(shù)據(jù)格式。其中大部分寫法都很好用,但也有一些寫法有問題。//整數(shù)
varcount=10;
//小數(shù)
varprice=10.0;
varprice=10.00;
//不推薦的小數(shù)寫法:沒有小數(shù)部分
varprice=10.;
//不推薦的小數(shù)寫法:沒有整數(shù)部分
varprice=.1;
//不推薦的寫法:八進制寫法已經(jīng)被棄用了
varnum=010;
//十六進制寫法
varnum=0xA2;
//科學計數(shù)法
varnum=1e23;前兩種有問題的寫法分別是省略了小數(shù)部分,比如10.,和省略了整數(shù)部分,比如.1。每種寫法都有同一個問題:很難搞清楚被省略小數(shù)點之前或之后的部分是不小心丟掉了還是刻意為之。很可能是開發(fā)者不小心漏掉了。因此為了避免歧義,請不要省略小數(shù)點之前或之后的數(shù)字。Dojo編程風格指南明確禁止這兩種寫法。JSLint和JSHint對這兩種寫法都會給出警告。最后一個有問題的寫法是八進制數(shù)字寫法。長久以來,JavaScript支持八進制數(shù)字寫法是很多錯誤和歧義的根源。數(shù)字直接量010不是表示10,而是表示八進制中的8。大多數(shù)開發(fā)者對八進制格式并不熟悉,也很少用到,所以最好的做法是在代碼中禁止八進制直接量。盡管在所有流行的編程規(guī)范中沒有關于此的規(guī)定,但在JSlint和JSHint中都會對八進制直接量給出警告。1.7.3nullnull是一個特殊值,但我們常常誤解它,將它和undefined搞混。在下列場景中應當使用null。用來初始化一個變量,這個變量可能賦值為一個對象。用來和一個已經(jīng)初始化的變量比較,這個變量可以是也可以不是一個對象。當函數(shù)的參數(shù)期望是對象時,用作參數(shù)傳入。當函數(shù)的返回值期望是對象時,用作返回值傳出。還有下面一些場景不應當使用null。不要使用null來檢測是否傳入了某個參數(shù)。不要用null來檢測一個未初始化的變量。這里有一些示例代碼。//好的用法
varperson=null;
//好的用法
functiongetPerson(){
if(condition){
returnnewPerson("Nicholas");
}else{
returnnull;
}
}
//好的用法
varperson=getPerson();
if(person!==null){
doSomething();
}
//不好的寫法:用來和未初始化的變量比較
varperson;
if(person!=null){
doSomething();
}
//不好的寫法:檢測是否傳入了參數(shù)
functiondoSomething(arg1,arg2,arg3,arg4){
if(arg4!=null){
doSomethingElse();
}
}理解null最好的方式是將它當做對象的占位符(placeholder)。這個規(guī)則在所有的主流編程規(guī)范中都沒有提及,但對于全局可維護性來說至關重要。關于null的陷阱會在第8章有更進一步的討論。1.7.4undefinedundefined是一個特殊值,我們常常將它和null搞混。其中一個讓人頗感困惑之處在于null==undefined結果是true。然而,這兩個值的用途卻各不相同。那些沒有被初始化的變量都有一個初始值,即undefined,表示這個變量等待被賦值。比如://不好的寫法
varperson;
console.log(person===undefined);//true盡管這段代碼能正常工作,但我建議避免在代碼中使用undefined。這個值常常和返回“undefined”的typeof運算符混淆。事實上,typeof的行為也很讓人費解,因為不管是值是undefined的變量還是未聲明的變量,typeof運算結果都是“undefined”。比如://foo未被聲明
varperson;
console.log(typeofperson);//"undefined"
console.log(typeoffoo);//"undefined"在這段代碼中,person和foo都會導致typeof返回“undefined”,哪怕person和foo在其他場景中的行為有天壤之別(在語句中使用foo會報錯,而使用person則不會報錯)。通過禁止使用特殊值undefined,可以有效地確保只在一種情況下typeof才會返回“undefined”:當變量未聲明時。如果你使用了一個可能(或可能不會)賦值為一個對象的變量時,則將其賦值為null。//好的做法
varperson=null;
console.log(person===null);//true將變量初始值賦值為null表明了這個變量的意圖,它最終很可能賦值為對象。typeof運算符運算null的類型時返回“object”,這樣就可以和undefined區(qū)分開了。1.7.5對象直接量創(chuàng)建對象最流行的一種做法是使用對象直接量,在直接量中直接寫出所有屬性,這種方式可以取代先顯式地創(chuàng)建Object的實例然后添加屬性的這種做法。比如,我們很少見到下面這種寫法。//不好的寫法
varbook=newObject();
book.title="MaintainableJavaScript";
book.author="NicholasC.Zakas";對象直接量允許你將所有的屬性都括在一對花括號內。直接量可以高效地完成非直接量寫法相同的任務,非直接量寫法語法看起來更復雜。當定義對象直接量時,常常在第一行包含左花括號,每一個屬性的名值對都獨占一行,并保持一個縮進,最后右花括號也獨占一行。比如://好的寫法
varbook={
title:"MaintainableJavaScript",
author:"NicholasC.Zakas"
};這種寫法在開源JavaScript代碼中能經(jīng)??吹?。盡管沒有歸納入文檔中,Google的JavaScript風格指南非常推薦使用這種寫法。Crockford的編程規(guī)范也推薦使用直接量代替Object構造函數(shù),但并沒有給出具體的書寫格式。1.7.6數(shù)組直接量和對象直接量類似,數(shù)組直接量是JavaScript中定義數(shù)組最簡潔的一種方式。不贊成顯式地使用Array構造函數(shù)來創(chuàng)建數(shù)組,比如://不好的寫法
varcolors=newArray("red","green","blue");
varnumbers=newArray(1,2,3,4);可以使用兩個方括號將數(shù)組初始元素括起來,來替代使用Array構造函數(shù)的方式來創(chuàng)建數(shù)組。//好的做法
varcolors=["red","green","blue"];
varnumbers=[1,2,3,4];在JavaScript中,這種模式非常常見。Google的JavaScript風格指南和Crockford的編程規(guī)范都推薦這樣做。\h[1]譯注:通常一個制表符長度相當于4個字符。\h[2]譯注:CamelCase和PascalCase都翻譯成“駝峰式大小寫”,但兩者含義有所不同,CamelCase包括“小駝峰式”和“大駝峰式”,在本章中,CamelCase被作者用來特指“小駝峰式大小寫”(即首字母小寫)命名法,PascalCase則特指“大駝峰式大小寫”(即首字母大寫)命名法,請讀者留意。
第2章注釋注釋是代碼中最常見的組成部分。它們是另一種形式的文檔,也是程序員最后才舍得花時間去寫的。但是,對于代碼的總體可維護性而言,注釋是非常重要的一環(huán)。打開一個沒有任何注釋的文件就好像趣味冒險,但如果給你的時間有限,這項任務就變成了折磨。適度的添加注釋可以解釋說明代碼的來龍去脈,其他開發(fā)者就可以不用從頭開始讀代碼,而是直接去讀代碼的任意部分。編程風格通常不會包含對注釋的風格約定,但我認為從注釋的作用即可看出它們的重要性不容忽視。JavaScript支持兩種不同類型的注釋:單行注釋和多行注釋。2.1單行注釋單行注釋以兩個斜線開始,以行尾結束。//這是一句單行注釋很多人喜歡在雙斜線后敲入一個空格,用來讓注釋文本有一定的偏移。單行注釋有三種使用方法。獨占一行的注釋,用來解釋下一行代碼。這行注釋之前總是有一個空行,且縮進層級和下一行代碼保持一致。在代碼行的尾部的注釋。代碼結束到注釋之間至少有一個縮進。注釋(包括之前的代碼部分)不應當超過單行最大字符數(shù)限制,如果超過了,就將這條注釋放置于當前代碼行的上方。被注釋掉的大段代碼(很多編輯器都可以批量注釋掉多行代碼)。單行注釋不應當以連續(xù)多行注釋的形式出現(xiàn),除非你注釋掉一大段代碼。只有當需要注釋一段很長的文本時才使用多行注釋。這里有一些示例代碼。//好的寫法
if(condition){
//如果代碼執(zhí)行到這里,則表明通過了所有安全性檢查
allowed();
}
//不好的寫法:注釋之前沒有空行
if(condition){
//如果代碼執(zhí)行到這里,則表明通過了所有安全性檢查
allowed();
}
//不好的寫法:錯誤的縮進
if(condition){
//如果代碼執(zhí)行到這里,則表明通過了所有安全性檢查
allowed();
}
//好的寫法
varresult=something+somethingElse;//somethingElse不應當取值為null
//不好的寫法:代碼和注釋之間沒有間隔
varresult=something+somethingElse;//somethingElse不應當取值為null
//好的寫法
//if(condition){
//doSomething();
//thenDoSomethingElse();
//}
//不好的寫法:這里應當用多行注釋
//接下來的這段代碼非常難,那么,讓我詳細解釋一下
//這段代碼的作用是首先判斷條件是否為真
//只有為真時才會執(zhí)行。這里的條件是通過
//多個函數(shù)計算出來的,在整個會話生命周期內
//這個值是可以被修改的
if(condition){
//如果代碼執(zhí)行到這里,則表明通過了所有安全性檢查
allowed();
}2.2多行注釋多行注釋可以包裹跨行文本。它以/*開始,以*/結束。多行注釋不僅僅可以用來包裹跨行文本,這取決于你。下面這些都是合法的注釋。/*我的注釋*/
/*另一段注釋
這段注釋包含兩行*/
/*
又是一段注釋
這段注釋同樣包含兩行
*/盡管從技術的角度看,這些注釋都是合法的,但我比較青睞Java風格的多行注釋。Java風格的注釋至少包含三行:第一行是/*,第二行是以*開始且和上一行的*保持左對齊,最后一行是*/。這種注釋看起來像下面這樣。/*
*另一段注釋
*這段注釋包含兩行文本
*/通過在注釋塊左側注上星號,會讓注釋更加清晰。有一些IDE(比如NetBean和Eclipse)會為你自動插入這些星號。多行注釋總是會出現(xiàn)在將要描述的代碼段之前,注釋和代碼之間沒有空行間隔。和單行注釋一樣,多行注釋之前應當有一個空行,且縮進層級和其描述的代碼保持一致。來看下面這段例子。//好的寫法
if(condition){
/*
*如果代碼執(zhí)行到這里
*說明通過了所有的安全性檢測
*/
allowed();
}
//不好的寫法:注釋之前無空行
if(condition){
/*
*如果代碼執(zhí)行到這里
*說明通過了所有的安全性檢測
*/
allowed();
}
//不好的寫法:星號后沒有空格
if(condition){
/*
*如果代碼執(zhí)行到這里
*說明通過了所有的安全性檢測
*/
allowed();
}
//不好的寫法:錯誤的縮進
if(condition){
/*
*如果代碼執(zhí)行到這里
*說明通過了所有的安全性檢測
*/
allowed();
}
//不好的寫法:代碼尾部注釋不要用多行注釋格式
varresult=something+somethingElse;/*somethingElse不應當取值為null*/2.3使用注釋何時添加注釋是程序員經(jīng)常爭論的一個話題。一種通行的指導原則是,當代碼不夠清晰時添加注釋,而當代碼很明了時不應當添加注釋。比如這個例子中,注釋是畫蛇添足。//不好的寫法
//初始化count
varcount=10;因為代碼中初始化count的操作是顯而易見的。注釋并沒有提供其他有價值的信息。換個角度講,如果這個值10具有一些特殊的含義,而且無法直接從代碼中看出來,這時就有必要添加注釋了。//好的寫法
//改變這個值可能會讓它變成青蛙
varcount=10;當然不可能因為修改了count的值它就變成了青蛙,但這的確是一個好的注釋寫法的例子,因為注釋中給出了必要的信息,如果沒有注釋,你不可能獲得這些信息。想象一下如果你修改了count的值它真的變成了青蛙,實在是讓人困惑不解,一切都源于你沒有寫這句注釋。因此,添加注釋的一般原則是,在需要讓代碼變得更清晰時添加注釋。2.3.1難于理解的代碼難于理解的代碼通常都應當加注釋。根據(jù)代碼的用途,你可以用單行注釋、多行注釋,或是混用這兩種注釋。關鍵是讓其他人更容易讀懂這段代碼。比如,這段示例代碼摘自YUI類庫中的Y.mix()方法。//好的寫法
if(mode){
/*
*當mode為2時(原型到原型,對象到對象),這里只遞歸執(zhí)行一次
*用來執(zhí)行原型到原型的合并操作。對象到對象的合并操作
*將會被掛起,在合適的時機執(zhí)行
*/
if(mode===2){
Y.mix(totype,totype,overwrite,
whitelist,0,merge);
}
/*
*根據(jù)指定的模式類型,我們可能會從源對象拷貝至原型中,
*或是從原型拷貝至接收對象中
*/
from=mode===1||mode===3?totype:supplier;
to=mode===1||mode===4?totype:receiver;
/*
*如果supplier或receiver不含有原型屬性時,
*則邏輯結束,并返回undefined。如果有原型屬性,
*則邏輯結束并返回receiver
*/
if(!from||!to){
returnreceiver;
}
}else{
from=supplier;
to=receiver;
}Y.mix()方法使用常量來決定如何處理。mode參數(shù)就表示這些常量其中之一,但僅僅通過這些數(shù)值無法理解它們各自代表的含義。這里的注釋非常棒,因為它及時地解釋了這里復雜的決策邏輯。2.3.2可能被誤認為錯誤的代碼另一個適合添加注釋的好時機是當代碼看上去有錯誤時。在團隊開發(fā)中,總是會有一些好心的開發(fā)者在編輯代碼時發(fā)現(xiàn)他人的代碼錯誤,就立即將它修復。有時這段代碼并不是錯誤的源頭,所以“修復”這個錯誤往往會制造其他錯誤,因此本次修改應當是可追蹤的。當你寫的代碼有可能會被別的開發(fā)者認為有錯誤時,則需要添加注釋。這里是另一段來自YUI源碼的例子。while(element&&(element=element[axis])){//提示:賦值操作
if((all||element[TAG_NAME])&&
(!fn||fn(element))){
returnelement;
}
}在這個例子中,開發(fā)者在while循環(huán)控制條件中使用了一個賦值運算符。這不是一種標準用法,并常常被檢測工具認為是有問題的。如果你對這段代碼不熟悉,讀到這段沒有注釋的代碼時,很可能誤以為這是一個錯誤,猜想作者的本意是使用比較運算符==而不是賦值運算符=。這行末尾的注釋說明作者是有意為之,即賦值而非比較。這樣,其他開發(fā)者在讀到這段代碼時就不會將它“修復”。2.3.3瀏覽器特性hackJavaScript程序員常常會編寫一些低效的、不雅的、徹頭徹尾的骯臟代碼,用來讓低級瀏覽器正常工作。實際上這種情形是一種特殊的“可能被誤認為錯誤的代碼”:這種不明顯的做瀏覽器特性Hack的代碼可能隱含一些錯誤。這里有個例子,是摘自YUI類庫的Y.DOM.contains()方法。varret=false;
if(!needle||!element||!needle[NODE_TYPE]||!element[NODE_TYPE]){
ret=false;
}elseif(element[CONTAINS]){
//如果needle不是ELEMENT_NODE時,IE和Safari下會有錯誤
if(Y.UA.opera||needle[NODE_TYPE]===1){
ret=element[CONTAINS](needle);
}else{
ret=Y_DOM._bruteContains(element,needle);
}
}elseif(element[COMPARE_DOCUMENT_POSITION]){//gecko
if(element===needle||!!(element[COMPARE_DOCUMENT_POSITION](needle)&16)){
ret=true;
}
}
returnret;這段代碼的第6行包含一條重要的注釋。盡管IE和Safari中都有內置方法contains(),但如果needle不是一個元素時,這個方法會報錯。所以只有當瀏覽器是Opera時才能用這個方法,其他瀏覽器中needle必須是一個元素(nodeType是1)。這里關于瀏覽器的說明同樣解釋了為什么需要一個if語句,這個注釋不僅確保將來不會被其他人誤改動,而且在代碼編寫者回過頭閱讀自己的這段代碼時,也會適時地針對新版本的IE和Safari的兼容情況做出調整。2.4文檔注釋從技術的角度講,文檔注釋并不是JavaScript的組成部分,但它們是一種普遍的實踐。文檔注釋有很多種格式,但最流行的一種格式來自于JavaDoc文檔格式:多行注釋以單斜線加雙星號(/**)開始,接下來是描述信息,其中使用@符號來表示一個或多個屬性。來看一段來自YUI的源碼的例子。/**返回一個對象,這個對象包含被提供對象的所有屬性。后一個對象的屬性會覆蓋前一個對象的屬性。傳入一個單獨的對象,會創(chuàng)建一個它的淺拷貝(shallowcopy)。如果需要深拷貝(deepcopy),請使用'clone()'
@methodmerge
@param{Object}被合并的一個或多個對象
@return{Object}一個新的合并后的對象
**/
Y.merge=function(){
varargs=arguments,
i=0,
len=args.length,
result={};
for(;i<len;++i){
Y.mix(result,args[i],true);
}
returnresult;
};YUI類庫使用它自己的一個名叫YUIDoc的工具來根據(jù)這些注釋生成文檔。但是,它的格式幾乎和JSDocToolkit(類庫無關的)一模一樣,在開源項目中JSDocToolkit的應用非常廣泛,包括Google內部的很多開源項目。YUIDoc和JSDocToolkit之間的重要區(qū)別是,YUIDoc同時支持文檔注釋中的HTML和Markdown格式,而JSDocToolkit只支持HTML。這里強烈推薦你使用文檔生成工具來為你的JavaScript生成文檔。JavaScript代碼注釋必須符合你所用的工具支持的格式,但很多文檔生成工具都支持JavaDoc風格的文檔注釋。當使用文檔注釋時,你應當確保對如下內容添加注釋。所有的方法應當對方法、期望的參數(shù)和可能的返回值添加注釋描述。所有的構造函數(shù)應當對自定義類型和期望的參數(shù)添加注釋描述。所有包含文檔化方法的對象如果一個對象包含一個或多個附帶文檔注釋的方法,那么這個對象也應當適當?shù)蒯槍ξ臋n生成工具添加文檔注釋。當然,注釋的詳細格式和用法最終還是由你所選擇的文檔生成工具決定的。
第3章語句和表達式在JavaScript中,諸如if和for之類的語句有兩種寫法,使用花括號包裹的多行代碼或者不使用花括號的單行代碼。比如://不好的寫法,盡管這是合法的JavaScript的代碼
if(condition)
doSomething();
//不好的寫法,盡管是合法的JavaScript代碼
if(condition)doSomething();
//好的寫法
if(condition){
doSomething();
}
//不好的寫法,盡管是合法的JavaScript代碼
if(condition){doSomething();}前兩種寫法的if語句中都沒有用花括號,這在Crockford的編程規(guī)范、jQuery核心風格指南、Google的JavaScript風格指南以及Dojo編程風格指南中都是明確禁止的。默認情況下,省略花括號在JSLint和JSHint中都會報警告。絕大多數(shù)JavaScript程序員都認可這樣一點:不論塊語句(blockstatement)包含多行代碼還是單行代碼,都應當總是使用花括號。因為省略花括號會造成一些困惑??匆幌逻@段代碼。if(condition)
doSomething();
doSomethingElse();從這段代碼中很難看出作者的真正意圖。顯然這里是有錯誤的,但這個錯誤到底是縮進錯誤(最后一行不應當有縮進),還是想執(zhí)行if語句中的第2行和第3行代碼卻丟失了花括號?我們不得而知。如果添加了花括號,這個錯誤就很容易發(fā)現(xiàn)。這里另外兩個包含錯誤代碼的例子。if(condition){
doSomething();
}
doSomethingElse();
if(condition){
doSomething();
doSomethingElse();
}在這兩段示例代碼中的錯誤是很明顯的,它們都有明顯的縮進錯誤。通過花括號可以很快看出作者的意圖,并做出合適的修改,但不會感覺自己修改了最初的代碼邏輯。所有的塊語句都應當使用花括號,包括:ifforwhiledo...while...try...catch...finally3.1花括號的對齊方式關于塊語句的另一個話題是花括號的對齊方式。有兩種主要的花括號對齊風格。第一種風格是,將左花括號放置在塊語句中第一句代碼的末尾\h[1],比如:if(condition){
doSomething();
}else{
doSomethingElse();
}JavaScript代碼的這種風格繼承自Java,這在Java編程語言的編程規(guī)范中有明確規(guī)定。在Crockford的編程規(guī)范、jQuery核心風格指南、SproutCore編程風格指南、Google的JavaScript風格指南以及Dojo編程風格指南中都出現(xiàn)過?;ɡㄌ柕牡诙N對齊風格是將左花括號放置于塊語句首行的下一行。比如:if(condition)
{
doSomething();
}
else
{
doSomethingElse();
}這種風格是隨著C#流行起來的,因為VisualStudio強制使用這種對齊方式。當前并無主流的JavaScript編程規(guī)范推薦這種風格,GoogleJavaScript風格指南明確禁止這種用法,以免導致錯誤的分號自動插入。我個人也推薦使用第一種花括號對齊格式。3.2塊語句間隔塊語句首行附近的空白行同樣是我們需要考慮的。塊語句間隔有三種主要的風格。第一種風格是,在語句名、圓括號和左花括號之間沒有空格間隔。if(condition){
doSomething();
}不少程序員喜歡這種風格,因為這種風格看起來很緊湊,而另一些人抱怨說這種緊湊風格實際上破壞了一些易讀性。Dojo編程風格指南推薦使用這種風格。第二種風格是,在括左圓括號之前和右圓括號之后各添加一個空格。比如:if(condition){
doSomething();
}有很多程序員青睞這種風格,因為語句類型和條件判斷更易讀。這種風格是Crockford的編程規(guī)范和GoogleJavaScript風格指南所推薦的。第三種風格是在左圓括號后和右圓括號前各添加一個空格,正如下面這段代碼所示。if(condition){
doSomething();
}jQuery核心風格指南文檔規(guī)定了這種風格,因為它使語句中的各個部分都非常清晰和易讀。我比較傾向于第二種風格,這種風格是第一種和第三種風格的折衷。3.3switch語句很多程序員對switch語句可謂愛恨交加。關于switch語句的格式和使用方式也是眾說紛紜。其中一些多樣性來自于switch語句的傳承,它源自C,但在Java和JavaScript中又沒有完全相同的語法。盡管語法相似,JavaScript中的switch語句的行為和在其他語言中是不一樣的:switch語句中可以使用任意類型值,任何表達式都可合法地用于case從句。但在其他語言中則必須使用原始值和常量。3.3.1縮進對于JavaScript程序員來說,switch語句的縮進格式是一個有爭議的話題。很多人使用Java風格的switch語句,看起來像下面這樣。switch(condition){
case"first":
//代碼
break;
case"second":
//代碼
break;
case"third":
//代碼
break;
default:
//代碼
}這種格式的獨特之處在于:每條case語句相對于switch關鍵字都縮進一個層級。從第二條case語句開始,每條case語句前后各有一個空行。幾乎所有的風格文檔都沒有規(guī)定何時使用這種格式的switch語句,主要是因為這種格式正是很多編輯器自動支持的。盡管我比較傾向于這種格式,但Crockford的編程規(guī)范和Dojo編程風格指南則提倡另一種格式,這種格式略有不同:switch(condition){
case"first":
//代碼
break;
case"second":
//代碼
break;
case"third":
//代碼
break;
default:
//代碼
}這種寫法和前一種寫法的主要不同之處在于,case關鍵字保持和switch關鍵字左對齊。同樣要注意,在語句中并沒有空行的存在。JSLint期望case和switch具有一致的縮進格式,如果case和switch縮進不一致時會報警告??梢酝ㄟ^“容忍不規(guī)則的間隔”開關選項來開啟或關閉JSLint的這項檢查。如果包含額外的空行,JSLint是不會報警告的。其他的編程風格對此沒有做規(guī)定,這個選擇完全是個人偏好問題。3.3.2case語句的“連續(xù)執(zhí)行”“執(zhí)行完一個case后連續(xù)執(zhí)行(fallthrough)下一個case”,這是否是一種廣被認可的實踐,也是備受爭議的一個問題。不小心省略case末尾的break是很多bug的罪魁禍首,因此D
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- T-ZGTX 27-2025 原生態(tài)雪域滑雪能力要求規(guī)范
- T-ZSM 0059-2024“領跑者”評價技術要求 數(shù)控圓鋸床
- 二零二五年度房屋租賃合同租賃雙方租賃期間租賃物租賃權法律適用協(xié)議
- 2025年度汽車行業(yè)代理招聘人才合作協(xié)議
- 2025年度餐廳員工勞動合同試用期規(guī)定
- 鋼結構合同補充協(xié)議(2025年度)安裝工程
- 二零二五年度危險品車輛運輸司機安全責任協(xié)議
- 2025年度食品飲料經(jīng)銷商授權及市場開發(fā)協(xié)議
- 二零二五年度借車車輛損失免責合同
- 二零二五年度雙方個人教育培訓合作協(xié)議
- 2024-2025年中國鋰電池隔膜行業(yè)未來發(fā)展趨勢分析及投資規(guī)劃建議研究報告
- 2024年南昌健康職業(yè)技術學院高職單招職業(yè)技能測驗歷年參考題庫(頻考版)含答案解析
- 2025浙江中煙招聘高頻重點提升(共500題)附帶答案詳解
- 月子會所護理人員禮儀
- 校園安全隱患排查培訓
- 《化妝品包裝材料相容性試驗評估指南》
- 無人機行業(yè)調查研究報告
- 2022版藝術新課標解讀心得(課件)小學美術
- 四川政采評審專家入庫考試基礎題復習試題
- 鋰離子電池失效分析及后果PFMEA-電子表格版
- 2024解析:第十九章生活用電-基礎練(解析版)
評論
0/150
提交評論