本文檔用做JavaScript編程語言中Google源代碼編碼標準的完整定義。JavaScript源文件被描述爲Google風格,當且僅當它符合此處的規則時。javascript
與其餘編程風格指南同樣,所涉及的問題不只包括格式化的美學問題,還包括其餘類型的約定或編碼標準。可是,本文檔主要關注咱們廣泛遵循的嚴格規則,並避免提供不明確可執行的建議(不管是經過人工仍是工具)。html
在本文件中,除非另有說明:java
術語註釋老是指實現註釋。咱們不使用文檔註釋
這一短語,而是使用經常使用術語「JSDoc」來表示人類可讀的文本和機器可讀的註釋 /** … */
。node
使用短語時,本樣式指南使用RFC 2119術語必須, 不得,應該,不該該,也能夠。術語偏好和 避免分別對應應該和不應該對應。命令性和陳述性陳述是規定性的,而且必須符合。git
其餘術語說明
將在整個文件中偶爾出現。es6
本文檔中的示例代碼是非規範性的。也就是說,雖然示例是Google風格,但它們可能並無說明表明代碼的惟一時尚方式。在示例中進行的可選格式選擇不得做爲規則強制執行。github
文件名必須所有小寫,而且能夠包含下劃線(_
)或短劃線(-
),但不包含其餘標點符號。遵循項目使用的約定。文件名的擴展名必須是.js
。算法
源文件以UTF-8編碼。shell
除了行終止符序列以外,ASCII水平空格字符(0x20)是惟一出如今源文件中任何位置的空白字符。這意味着express
字符串文字中的全部其餘空白字符都被轉義,而且
製表符不用於縮進。
對於具備特殊的轉義序列(任何字符\'
,\"
,\\
,\b
, \f
,\n
,\r
,\t
,\v
),該序列使用,而不是對應的數字逃逸(例如\x0a
,\u000a
或\u{a}
)。傳統的八進制轉義從未使用過。
對於剩餘的非ASCII字符,使用實際的Unicode字符(例如∞
)或等效的十六進制或Unicode轉義(例如\u221e
),這取決於哪些使代碼更易於閱讀和理解。
提示:在Unicode轉義狀況下,有時即便使用實際的Unicode字符,解釋性註釋也會很是有用。
例 | 討論 |
---|---|
const units = 'μs'; |
最佳:即便沒有評論也徹底清楚。 |
const units = '\u03bcs'; // 'μs' |
容許,但沒有理由這樣作。 |
const units = '\u03bcs'; // Greek letter mu, 's' |
容許,但尷尬,容易出錯。 |
const units = '\u03bcs'; |
差:讀者不知道這是什麼。 |
return '\ufeff' + content; // byte order mark |
好:對不可打印的字符使用轉義,並在必要時進行註釋。 |
提示:因爲擔憂某些程序可能沒法正確處理非ASCII字符,所以不要讓代碼的可讀性下降。若是發生這種狀況,那些程序就會被破壞,必須修復它們。
源文件按順序包含:
@fileoverview
JSDoc,若是存在的話goog.module
聲明goog.require
聲明除了文件的實現以外,正好有一個空行用於分隔存在的每一個部分,其前面可能有1或2個空行。
若是許可或版權信息屬於文件,則屬於此處。
@fileoverview
JSDoc,若是存在的話有關格式規則,請參見7.5頂級/文件級註釋。
goog.module
聲明全部文件必須goog.module
在一行中只聲明一個名稱:包含goog.module
聲明的行不得包裝,所以是80列限制的例外。
goog.module的整個參數是定義命名空間的。它是包名稱(反映代碼所在目錄結構片斷的標識符),以及它定義鏈接到結尾的主類/枚舉/接口。
例
goog.module( 'search.urlHistory.UrlHistoryService');
模塊命名空間可能永遠不會被命名爲另外一個模塊命名空間的直接子節點。
非法:
goog.module( 'foo.bar'); //'foo.bar.qux'會好的 goog.module( 'foo.bar.baz');
目錄層次結構反映了命名空間層次結構,所以深層嵌套子節點是更高級別父目錄的子目錄。請注意,這意味着「父」命名空間組的全部者必須知道全部子命名空間,由於它們存在於同一目錄中。
goog.setTestOnly
單個goog.module
語句能夠選擇性地跟隨對goog.setTestOnly()的調用。
goog.module.declareLegacyNamespace
單個goog.module
語句能夠選擇性地跟隨調用 goog.module.declareLegacyNamespace();
。goog.module.declareLegacyNamespace()
儘量避免 。
例:
goog.module( 'my.test.helpers'); goog.module.declareLegacyNamespace(); goog.setTestOnly();
goog.module.declareLegacyNamespace
存在是爲了簡化從傳統的基於對象層次結構的命名空間的轉換,但帶有一些命名限制。因爲必須在父命名空間以後建立子模塊名稱,所以該名稱不能是任何其餘名稱的子項或父項 goog.module
(例如,goog.module('parent');
而且 goog.module('parent.child');
不能同時安全存在,也不能 goog.module('parent');
和goog.module('parent.child.grandchild');
)。
不要使用ES6模塊(即export
和import
關鍵字),由於它們的語義還沒有最終肯定。請注意,一旦語義徹底標準化,將從新審視此策略。
goog.require
陳述使用goog.require
語句完成導入,在模塊聲明後當即組合在一塊兒。每一個都goog.require
被分配給一個常量別名,或者被解構爲幾個常量別名。這些別名是引用require
d依賴關係的惟一可接受的方式,不管是在代碼中仍是在類型註釋中:除非做爲參數,不然永遠不會使用徹底限定名goog.require
。別名應儘量與導入模塊名稱的最終點分隔組件匹配,但可能包含其餘組件(若是須要消除歧義,或者若是顯着改進,則使用適當的套管使別名'套管仍能正確識別其類型)可讀性。goog.require
語句可能不會出如今文件中的任何其餘位置。
若是僅爲其反作用導入模塊,則能夠省略分配,但徹底限定的名稱可能不會出如今文件中的任何其餘位置。須要註釋來解釋爲何須要這樣作並抑制編譯器警告。
這些行按照如下規則排序:全部要求在左側的名稱首先出現,按字母順序按這些名稱排序。而後解構須要,再次按左側的名稱排序。最後,任何goog.require
獨立的調用(一般這些是針對其反作用導入的模塊)。
提示:無需記住此訂單並手動強制執行。您能夠依靠IDE來報告未正確排序的需求。
若是長別名或模塊名稱會致使行超過80列限制,則不得包裝:goog.require行是80列限制的例外。
例:
const MyClass = goog.require('some.package.MyClass'); const NsMyClass = goog.require('other.ns.MyClass'); const googAsserts = goog.require('goog.asserts'); const testingAsserts = goog.require('goog.testing.asserts'); const than80columns = goog.require('pretend.this.is.longer.than80columns'); const {clear,forEach,map} = goog.require('goog.array'); / ** @suppress {extraRequire}初始化MyFramework。* / goog.require( 'my.framework.initialization');
非法:
const randomName = goog.require('something.else'); //名稱必須匹配 const {clear,forEach,map} = //不要破壞行 goog.require( 'goog.array'); function someFunction(){ const alias = goog.require('my.long.name.alias'); //必須在頂級 // ... }
goog.forwardDeclare
goog.forwardDeclare
不常常須要,但它是打破循環依賴或引用後期加載代碼的有用工具。這些陳述組合在一塊兒,並緊跟任何goog.require
陳述。一個 goog.forwardDeclare
語句必須遵循相同的樣式規則的 goog.require
聲明。
在聲明全部依賴性信息以後(由至少一個空行分隔)以後的實際實現。
這可能包括任何模塊本地聲明(常量,變量,類,函數等),以及任何導出的符號。
術語注意:相似塊的構造是指類,函數,方法或大括號分隔的代碼塊的主體。請注意,經過 5.2數組文字和5.3對象文字,能夠選擇將任何數組或對象文字視爲相似於塊的結構。
提示:使用clang-format
。JavaScript社區已投入精力確保clang格式在JavaScript文件上作正確的事情
。clang-format
與幾位受歡迎的編輯集成。
大括號都須要全部的控制結構(即if
,else
,for
,do
, while
,以及任何其餘),即便身體只包含一個聲明。非空塊的第一個語句必須從它本身的行開始。
非法:
if(someVeryLongCondition()) 作一點事(); for(let i = 0; i <foo.length; i ++)bar(foo [i]);
例外:一個簡單的if語句能夠徹底放在一行而沒有包裝(而且沒有else)能夠保留在一行中,當它提升可讀性時沒有括號。這是控制結構能夠省略大括號和換行符的惟一狀況。
if(shortCondition())返回;
大括號遵循Kernighan和Ritchie風格(埃及括號
),用於 非空塊和塊狀結構:
else
,catch
, while
,或逗號,分號,或右括號。例:
class InnerClass { 構造函數(){} / ** @param {number} foo * / method(foo){ if(condition(foo)){ 嘗試{ //注意:這可能會失敗。 東西(); } catch(err){ 恢復(); } } } }
空塊或塊狀結構能夠在打開後當即關閉,其間沒有字符,空格或換行符(即{}
), 除非它是多塊語句的一部分(直接包含多個塊的語句):if
/ else
或try
/ catch
/ finally
)。
例:
function doNothing(){}
非法:
if(condition){ // ... } else if(otherCondition){} else { // ... } 嘗試{ // ... } catch(e){}
每次打開新的塊或塊狀構造時,縮進都會增長兩個空格。當塊結束時,縮進返回到先前的縮進級別。縮進級別適用於整個塊中的代碼和註釋。(參見4.1.2非空塊中的示例:K&R樣式)。
塊狀
任何數組文字均可以選擇格式化,就好像它是一個「塊狀結構」。例如,如下都是有效的(不是詳盡的列表):
const a = [ 0, 1, 2, ]。 const b = [0,1,2];
const c = [0,1,2]; someMethod(foo,[ 0,1,2, ],酒吧);
容許其餘組合,特別是在強調元素之間的語義分組時,但不該僅用於減小較大數組的垂直大小。
塊狀
任何對象文字均可以選擇格式化,就好像它是一個「塊狀結構」。相同的例子適用於4.2.1數組文字:可選的塊狀。例如,如下都是有效的(不是詳盡的列表):
const a = { a:0, b:1, }; const b = {a:0,b:1};
const c = {a:0,b:1}; someMethod(foo,{ a:0,b:1, },酒吧);
類文字(不管是聲明仍是表達式)縮進爲塊。不要在方法以後添加分號,或者在類聲明的右括號以後添加分號 (包含類表達式的賦值 - 如賦值 - 仍以分號結尾)。除非類擴展了模板化類型,不然請使用extends
關鍵字,而不是 @extends
JSDoc註釋。
例:
class Foo { constructor(){ / ** @type {number} * / this.x = 42; } / ** @return {number} * / 方法() { 返回this.x; } } Foo.Empty = class {};
/ ** @extends {Foo <string>} * / foo.Bar = class extends Foo { / ** @override * / 方法() { return super.method()/ 2; } }; / ** @interface * / class Frobnicator { / ** @param {string}消息* / frobnicate(消息){} }
在函數調用的參數列表中聲明匿名函數時,函數體將比前一個縮進深度縮進兩個空格。
例:
prefix.something.reallyLongFunctionName('whatever',(a1,a2)=> { //相對於縮進深度縮進函數體+2 //上面一行的'prefix'語句。 if(a1.equals(a2)){ someOtherLongFunctionName(A1); } else { andNowForSomethingCompletelyDifferent(a2.parrot); } }); some.reallyLongFunctionCall(arg1,arg2,arg3) .thatsWrapped() .then((result)=> { //相對於縮進深度縮進函數體+2 //'.then()'調用。 if(result){ result.use(); } });
與任何其餘塊同樣,開關塊的內容縮進爲+2。
在切換標籤以後,出現換行符,而且縮進級別增長+2,就像打開一個塊同樣。若是詞法做用域須要,可使用顯式塊。如下開關標籤返回到先前的縮進級別,就像塊已關閉同樣。
a break
和如下狀況之間的空行是可選的。
例:
開關(動物){ case Animal.BANDERSNATCH: handleBandersnatch(); 打破; case Animal.JABBERWOCK: handleJabberwock(); 打破; 默認: 拋出新錯誤('未知動物'); }
每一個語句後面都有一個換行符。
每一個語句必須以分號結束。禁止依賴自動分號插入。
JavaScript代碼的列限制爲80個字符。除非另有說明,不然任何超出此限制的行都必須換行,如 4.5換行中所述。
例外:
goog.module
和goog.require
語句(參見3.3 goog.module語句和 3.4 goog.require語句)。術語注意:換行定義爲將單個表達式分紅多行。
有沒有表現出全面的,肯定性的公式究竟如何線路纏繞在每一種狀況。一般有幾種有效的方法來包裝同一段代碼。
注意:雖然換行的典型緣由是爲了不溢出列限制,但即便是實際符合列限制的代碼也可能由做者自行決定是否換行。
提示:提取方法或局部變量能夠解決問題,而無需換行。
換行的主要指令是:更喜歡在更高的句法層面打破。
首選:
currentEstimate = calc(currentEstimate + x * currentEstimate)/ 2.0F;
灰心:
currentEstimate = calc(currentEstimate + x * currentEstimate)/ 2.0f;
在前面的例子中,從最高到最低的句法級別以下:賦值,除法,函數調用,參數,數字常量。
運算符包含以下:
dot(
.
),它實際上不是運算符。(
)。,
)保持附加到其前面的標記。注意:換行的主要目標是擁有清晰的代碼,而不必定是適合最小行數的代碼。
當換行時,第一行(每一個延續行)以後的每一行從原始行縮進至少+4,除非它屬於塊縮進的規則。
當存在多個連續線時,壓痕能夠適當地變化超過+4。一般,更深層次的句法級別的連續行用4的較大倍數縮進,當且僅當它們以語法並行元素開頭時,兩行使用相同的縮進級別。
4.6.3水平對齊:不鼓勵使用可變數量的空格來阻止某些令牌與前一行對齊。
出現一個空白行:
容許多個連續的空白行,但從不須要(也不鼓勵)。
水平空白的使用取決於位置,並分爲三大類:前導(在行的開頭),尾隨(在行的末尾)和內部。領先的空白(即縮進)在別處獲得解決。禁止使用尾隨空格。
除了語言或其餘樣式規則所要求的內容以外,除了文字,註釋和JSDoc以外,單個內部ASCII空間也僅出如今如下位置。
if
,for
或catch
)從一個開括號((
它後面在該行)。else
或catch
)與}
該行前面的右大括號()分開。{
)以前,有兩個例外:
foo({a: [{c: d}]})
)。abc${1 + 2}def
)。,
)或分號(;
)以後。請注意,在這些字符以前毫不容許使用空格。:
對象文字中冒號()以後。//
)的兩側開始一行結束註釋。這裏容許多個空格,但不是必需的。this.foo = /** @type {number} */ (bar);
或function(/** string */ foo) {
)。術語注意:水平對齊是在代碼中添加可變數量的附加空格的作法,目的是使某些標記直接出如今前一行的某些其餘標記下方。
這種作法是容許的,但谷歌風格一般不鼓勵這種作法。甚至不須要在已經使用過的地方保持水平對齊。
這是一個沒有對齊的示例,後面跟一個對齊。二者都是容許的,但後者是不鼓勵的:
{ 微小的:42,//這很棒 更長:435,//這也是 }; { 微小:42,//容許,但將來的編輯 更長:435,//可能會使它不對齊 };
提示:對齊能夠提升可讀性,但會爲未來的維護帶來問題。考慮一下須要觸及一條線的將來變化。這種改變可能會使之前使人愉悅的格式化變得嚴重,而且這是容許的。更常見的是,它會提示編碼人員(也許你)調整附近行的空白,這可能會引起一連串的從新格式化。那個單線變化如今有一個爆炸半徑。
這可能會致使無心義的忙碌工做,但最多它仍然會破壞版本歷史信息,減慢審閱者的速度並加重合並衝突。
但願將全部函數參數放在與函數名稱相同的行上。若是這樣作會超過80列限制,則參數必須以可讀的方式換行。爲了節省空間,您能夠儘量接近80,或者將每一個參數放在本身的行上以加強可讀性。縮進應該是四個空格。容許對齊括號,但不鼓勵。如下是參數包裝最多見的模式:
//參數從一個新行開始,縮進四個空格。首選的時候 //參數與函數名稱(或關鍵字)不在同一行 //「功能」)但徹底適合第二行。工做時間很長 //函數名稱,無需從新定義便可重命名,空間不足。 作一點事( descriptiveArgumentOne,descriptiveArgumentTwo,descriptiveArgumentThree){ // ... } //若是參數列表較長,請換行80.使用較少的垂直空間, //但違反了矩形規則,所以不推薦使用。 doSomething(veryDescriptiveArgumentNumberOne,veryDescriptiveArgumentTwo, tableModelEventHandlerProxy,artichokeDescriptorAdapterIterator){ // ... } //四個空格,每行一個參數。使用長函數名稱, //倖存重命名,並強調每一個參數。 作一點事( veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator){ // ... }
只有看成者和審閱者贊成沒有合理的機會在沒有它們的狀況下錯誤解釋代碼時,纔會省略可選的分組括號,也不會使代碼更易於閱讀。它是不是 合理的假設,每一個讀者有記憶的整個運算符優先級表。
如下不要使用沒必要要的括號整個表達式 delete
,typeof
,void
,return
,throw
,case
,in
,of
,或yield
。
類型轉換須要括號:/** @type {!Foo} */ (foo)
。
本節介紹實現註釋。JSDoc在7 JSDoc中單獨解決。
塊註釋與周圍代碼的縮進級別相同。他們多是/* … */
或者是//
風格。對於多行/* … */
註釋,後續行必須以*與*
前一行的對齊開頭,以使註釋顯而易見,沒有額外的上下文。只要值和方法名稱沒有充分表達含義,「參數名稱」註釋就應出如今值以後。
/ * * 這是 * 好的。 * / // 因此 // 這是。 / *這也很好。* / someFunction(obviousParam,true / * shouldRender * /,'hello'/ * name * /);
註釋不包含在用星號或其餘字符繪製的框中。
不要將JSDoc(/** … */
)用於上述任何實現註釋。
JavaScript包含許多可疑(甚至危險)的功能。本節描述了可能使用或未使用的功能,以及對其使用的任何其餘限制。
const
和let
聲明全部的局部變量有兩種const
或let
。除非須要從新分配變量,不然默認使用const。將var
不得使用關鍵字。
每一個局部變量聲明只聲明一個變量:聲明,例如let a = 1, b = 2;
未使用。
局部變量不能習慣性地在其包含塊或塊狀結構的開始申報。相反,局部變量被聲明爲接近它們首次使用的點(在合理範圍內),以最小化它們的範圍。
能夠在聲明上方的行上添加JSDoc類型註釋,或者在變量名稱以前添加內聯。
例:
const / **!Array <number> * / data = []; / ** @type {!Array <number>} * / const data = [];
提示:在許多狀況下,編譯器能夠推斷模板化類型而不是其參數。當初始化文字或構造函數調用不包含模板參數類型的任何值(例如,空數組,對象,Map
s或Set
s),或者在閉包中修改變量時,尤爲如此。在這些狀況下,局部變量類型註釋特別有用,不然編譯器會將模板參數推斷爲未知。
每當最終元素和結束括號之間存在換行符時,請包含尾隨逗號。
例:
const值= [ '第一價值', '第二價值', ]。
Array
構造函數若是添加或刪除參數,則構造函數容易出錯。請改用文字。
非法:
const a1 = new Array(x1,x2,x3); const a2 = new Array(x1,x2); const a3 = new Array(x1); const a4 = new Array();
除了第三種狀況以外,這能夠正常工做:if x1
是一個整數而後 a3
是x1
全部元素都是大小的數組undefined
。若是x1
是任何其餘數字,那麼將拋出異常,若是它是其餘任何東西,那麼它將是單元素數組。
相反,寫
const a1 = [x1,x2,x3]; const a2 = [x1,x2]; const a3 = [x1]; const a4 = [];
new Array(length)
適當時容許使用顯式分配給定長度的數組。
不要在數組上定義或使用非數字屬性(除了 length
)。使用Map
(或Object
)代替。
能夠在賦值的左側使用數組文字來執行解構(例如,從單個數組或可迭代解包多個值時)。能夠包括最終的rest
元素(...
在變量名和變量名之間沒有空格 )。若是未使用元素,則應省略元素。
const [a,b,c,... rest] = generateResults(); 讓[,b ,, d] = someArray;
解構也能夠用於函數參數(請注意,參數名稱是必需的可是被忽略)。[]
若是結構化數組參數是可選的,則始終指定爲默認值,並在左側提供默認值:
/ ** @param {!Array <number> =} param1 * / function optionalDestructuring([a = 4,b = 2] = []){...};
非法:
function badDestructuring([a,b] = [4,2]){...};
提示:對於(un)將多個值打包到函數的參數或返回中,儘量將對象解構更改成數組解構,由於它容許命名單個元素併爲每一個元素指定不一樣的類型。*
數組文字能夠包括擴展運算符(...
)以展平一個或多個其餘迭代中的元素。應該使用擴展運算符而不是更笨拙的構造Array.prototype
。以後沒有空間 ...
。
例:
[... foo] //首選Array.prototype.slice.call(foo) [... foo,... bar] //首選foo.concat(bar)
每當最終屬性和右大括號之間存在換行符時,請包含尾隨逗號。
Object
構造函數雖然Object
沒有一樣的問題Array
,但仍然不容許一致性。請改用對象文字({}
或{a: 0, b: 1, c: 2}
)。
對象文字能夠表示結構(帶有不帶引號的鍵和/或符號)或dicts(帶引號和/或計算鍵)。不要在單個對象文字中混合使用這些鍵類型。
非法:
{ a:42,// struct-style unquoted key 'b':43,// dict-style引用鍵 }
{['key' + foo()]: 42}
容許使用計算屬性名稱(例如),而且將其視爲dict樣式(引用)鍵(即,不得與非引用鍵混合),除非計算屬性是符號(例如,[Symbol.iterator]
)。枚舉值也可用於計算鍵,但不該與同一文字中的非枚舉鍵混合使用。
可使用方法shorthand({method() {… }}
)代替冒號後面緊跟一個function
或箭頭函數文字,在對象文字上定義方法。
例:
返回{ 東西:'糖果', 方法() { 歸還這個。//返回'candy' }, };
請注意,this
在方法簡寫中或function
引用對象文字自己,而this
在箭頭函數中引用對象文字以外的範圍。
例:
class { getObjectLiteral(){ this.stuff ='fruit'; 返回{ 東西:'糖果', 方法:()=> this.stuff,//返回'fruit' }; } }
對象文字容許使用速記屬性。
例:
const foo = 1; const bar = 2; const obj = { FOO, 酒吧, method(){return this.foo + this.bar; }, }; assertEquals(3,obj.method());
能夠在賦值的左側使用對象解構模式來執行解構並從單個對象解包多個值。
結構化對象也能夠用做函數參數,但應儘量簡單:單個級別的不帶引號的速記屬性。嵌套和計算屬性的更深層次可能不會用於參數解構。在destructured參數的左側指定任何默認值({str = 'some default'} = {}
而不是{str} = {str: 'some default'}
),若是解構對象自己是可選的,則必須默認爲{}
。能夠爲destructured參數提供JSDoc任何名稱(名稱未使用但編譯器須要)。
例:
/ ** * @param {string}普通 * @param {{num:(number | undefined),str:(string | undefined)} =} param1 * num:作某事的次數。 * str:用來作東西的字符串。 * / 函數destructured(普通,{num,str ='some default'} = {})
非法:
/ ** @param {{x:{num:(number | undefined),str:(string | undefined)}}} param1 * / function nestedTooDeeply({x:{num,str}}){}; / ** @param {{num:(number | undefined),str:(string | undefined)} =} param1 * / function nonShorthandProperty({num:a,str:b} = {}){}; / ** @param {{a:number,b:number}} param1 * / function computedKey({a,b,[a + b]:c}){}; / ** @param {{a:number,b:string} =} param1 * / function nontrivialDefault({a,b} = {a:2,b:4}){};
解構也能夠用於goog.require
語句,在這種狀況下不能包裝:整個語句佔用一行,不管它有多長(參見3.4 goog.require語句)。
經過將@enum
註釋添加到對象文字來定義枚舉。定義後,可能沒法將其餘屬性添加到枚舉中。枚舉必須是常量,全部枚舉值必須是不可變的。
/ ** *支持的溫標。 * @enum {string} * / const TemperatureScale = { CELSIUS:'攝氏', FAHRENHEIT:'華氏', }; / ** *枚舉有兩個選項。 * @enum {number} * / const Option = { / **使用的選項應該是第一個。* / FIRST_OPTION:1, / **兩個選項中的第二個。* / SECOND_OPTION:2, };
具體類的構造函數是可選的。子類構造函數必須super()
在設置任何字段或以其餘方式訪問以前調用 this
。接口不能定義構造函數。
在構造函數中設置全部具體對象的字段(即除方法以外的全部屬性)。註釋永遠不會從新分配的字段@const
(這些字段沒必要很是不可變)。必須使用私有字段進行註釋, @private
而且其名稱必須以尾隨下劃線結尾。字段永遠不會設置在具體類上prototype
。
例:
class Foo { constructor(){ / ** @private @const {!Bar} * / this.bar_ = computeBar(); } }
提示:在構造函數完成後,毫不應將屬性添加到實例或從實例中刪除屬性,由於它會顯着阻礙VM的優化能力。若有必要,稍後初始化的字段應undefined
在構造函數中顯式設置爲以防止之後的形狀更改。添加 @struct
到對象將檢查未添加/訪問未聲明的屬性。默認狀況下,類會添加此類。
當屬性是符號時,計算屬性只能在類中使用。不容許使用Dict樣式屬性(即,引用或計算的非符號鍵,如5.3.3中所定義,不混合引用和不帶引號的鍵)。[Symbol.iterator]
應該爲邏輯上可迭代的任何類定義一個 方法。除此以外,Symbol
應謹慎使用。
提示:請當心使用任何其餘內置符號(例如Symbol.isConcatSpreadable
),由於它們不是由編譯器填充的,所以不適用於舊版瀏覽器。
在不干擾可讀性的地方,更喜歡模塊本地函數而不是私有靜態方法。
靜態方法只應在基類自己上調用。不該該在包含動態實例的變量上調用靜態方法,動態實例能夠是構造函數或子類構造函數(@nocollapse
若是已完成,則必須定義 ),而且不能直接在未定義方法的子類上調用自己。
非法:
class Base {/ ** @nocollapse * / static foo(){}} class Sub擴展Base {} function callFoo(cls){cls.foo(); } //勸阻:不要動態調用靜態方法 Sub.foo(); //非法:不要在沒有本身定義它的子類上調用靜態方法
雖然ES6類是首選,但有些狀況下ES6類可能不可行。例如:
若是存在或將存在子類(包括建立子類的框架),則沒法當即將其更改成使用ES6類語法。若是這樣的類使用ES6語法,則須要修改全部不使用ES6類語法的下游子類。
this
在調用超類構造函數以前須要已知值的框架,由於具備ES6超類的構造函數this
在調用super
返回以前無權訪問實例值。
在全部其餘方面的風格指南仍然適用於這樣的代碼:let
,const
,默認參數,休息和箭頭的功能都應該用在適當的時候。
goog.defineClass
容許相似於ES6類語法的類類定義:
設C = goog.defineClass(S,{ / ** * @param {string}值 * / 構造函數(值){ S.call(this,2); / ** @const * / this.prop = value; }, / ** * @param {string} param * @return {number} * / method(param){ 返回0; }, });
或者,雖然goog.defineClass
應該首選全部新代碼,但也容許使用更傳統的語法。
/ ** * @constructor @extends {S} * @param {string}值 * / 函數C(值){ S.call(this,2); / ** @const * / this.prop = value; } goog.inherits(C,S); / ** * @param {string} param * @return {number} * / C.prototype.method = function(param){ 返回0; };
若是存在超類,則應在調用超類構造函數以後在構造函數中定義每實例屬性。應該在構造函數的原型上定義方法。
正肯定義構造函數原型層次結構比首次出現更難!所以,最好是使用goog.inherits
來自Closure庫。
prototype
直接操縱sclass
與定義prototype
屬性相比,該關鍵字容許更清晰,更易讀的類定義。普通的實現代碼沒有業務操做這些對象,儘管它們仍然有用於定義5.4.5舊式類聲明中定義的@record
接口和類。Mixins和修改內置對象的原型是明確禁止的。
例外:框架代碼(如Polymer或Angular)可能須要使用prototype
s,而且不該採用更差的解決方法來避免這樣作。
例外:在接口中定義字段(參見5.4.9接口)。
不要使用JavaScript getter和setter屬性。它們可能使人驚訝且難以推理,而且在編譯器中的支持有限。改成提供普通方法。
例外:使用數據綁定框架(例如Angular和Polymer)時,可能會謹慎使用getter和setter。但請注意,編譯器支持是有限的。使用它們時,必須使用get foo()
和set foo(value)
在類或對象文字中定義它們,或者若是不可能,則使用Object.defineProperties
。不要使用 Object.defineProperty
,這會干擾屬性重命名。吸氣劑 不得改變可觀察狀態。
非法:
class Foo { get next(){return this.nextId ++; } }
該toString
方法可能被覆蓋,但必須始終成功,而且永遠不會有可見的反作用。
提示:特別要注意從toString調用其餘方法,由於異常條件可能致使無限循環。
接口能夠用@interface
或聲明@record
。聲明的接口@record
能夠@implements
由類或對象文字顯式(即經過)或隱式實現。
接口上的全部非靜態方法體必須是空塊。必須在接口體以後定義字段做爲存根prototype
。
例:
/ ** *可使用的東西。 * @record * / class Frobnicator { / ** *根據給定的策略執行frobnication。 * @param {!FrobnicationStrategy}策略 * / frobnicate(策略){} } / ** @type {number}放棄以前的嘗試次數。* / Frobnicator.prototype.attempts;
導出的函數能夠直接在exports
對象上定義,也能夠在本地聲明並單獨導出。鼓勵使用非導出功能,不該聲明@private
。
例子:
/ ** @return {number} * / function helperFunction(){ 返回42; } / ** @return {number} * / function exportedFunction(){ return helperFunction()* 2; } / ** * @param {string} arg * @return {number} * / function anotherExportedFunction(arg){ return helperFunction()/ arg.length; } / ** @const * / exports = {exportedFunction,anotherExportedFunction};
/ ** @param {string} arg * / exports.foo =(arg)=> { //作一些事...... };
函數可能包含嵌套函數定義。若是爲函數指定名稱頗有用,則應將其分配給本地const
。
箭頭函數提供了簡潔的語法並修復了許多困難 this
。首選箭頭函數優先於function
關鍵字,特別是嵌套函數(但請參見5.3.5方法簡寫)。
喜歡使用箭頭功能f.bind(this)
,特別是結束 goog.bind(f, this)
。避免寫做const self = this
。箭頭函數對於回調特別有用,回調有時會傳遞意外的附加參數。
箭頭的右側能夠是單個表達式或塊。若是隻有一個非破壞參數,則參數周圍的括號是可選的。
提示:即便對於單參數箭頭也使用括號是一個好習慣,由於若是在添加其餘參數時忘記括號,代碼仍然能夠合理地解析(但不正確)。
生成器啓用了許多有用的抽象,能夠根據須要使用。
定義生成器函數時,在存在時*
將function
關鍵字附加到關鍵字,並使用函數名稱中的空格將其分隔。當使用委託收益,附加*
的yield
關鍵字。
例:
/ ** @return {!Iterator <number>} * / function * gen1(){ 產量42; } / ** @return {!Iterator <number>} * / const gen2 = function *(){ yield * gen1(); } class SomeClass { / ** @return {!Iterator <number>} * / * gen(){ 產量42; } }
必須在函數定義以前的JSDoc中使用JSDoc註釋鍵入函數參數,但在相同簽名@override
s 的狀況下除外,其中省略全部類型。
參數類型能夠在參數名稱以前的內聯中指定(如(/** number */ foo, /** string */ bar) => foo + bar
)。內聯和 @param
類型註釋不能在同一個函數定義中混合使用。
使用參數列表中的equals運算符容許使用可選參數。可選參數必須包含equals運算符兩側的空格,其名稱與所需參數徹底相同(即不帶前綴 opt_
),=
在JSDoc類型中使用後綴,在必需參數以後使用,而不使用產生可觀察反作用的初始值設定項。全部可選參數必須在函數聲明中具備默認值,即便該值爲undefined
。
例:
/ ** * @param {string}必需此參數始終是必需的。 * @param {string =} optional此參數能夠省略。 * @param {!Node =} node另外一個可選參數。 * / 函數maybeDoSomething(必需,可選='',node = undefined){}
謹慎使用默認參數。當有少數幾個可選參數沒有天然順序時,首選解構(如在5.3.7解構中 )以建立可讀API。
注意:與Python的默認參數不一樣,可使用返回新的可變對象(例如{}
或[]
)的初始化器,由於每次使用默認值時都會對初始化程序進行求值,所以不會在調用之間共享單個對象。
提示:雖然包含函數調用的任意表達式能夠用做初始化程序,但這些表達式應儘量簡單。避免暴露共享可變狀態的初始化器,由於這很容易在函數調用之間引入意外耦合。
使用rest參數而不是訪問arguments
。Rest參數...
在其JSDoc中使用前綴鍵入。rest參數必須是列表中的最後一個參數。...
參數名稱和參數名稱之間沒有空格。不要命名rest參數var_args
。永遠不要命名局部變量或參數arguments
,這會混淆內置名稱。
例:
/ ** * @param {!Array <string>}數組這是一個普通的參數。 * @param {... number}數字參數的其他部分都是數字。 * / function variadic(array,... numbers){}
函數返回類型必須在函數定義正上方的JSDoc中指定,除非在相同簽名@override
的狀況下,全部類型都被省略。
必要時@template TYPE
在類定義上方的JSDoc中聲明泛型函數和方法。
函數調用可使用spread運算符(...
)。Function.prototype.apply
當數組或iterable被解包爲可變函數的多個參數時,首選擴展運算符。以後沒有空間...
。
例:
function myFunction(... elements){} myFunction(... array,... iterable,... generator());
普通字符串文字用單引號('
)分隔,而不是雙引號("
)。
提示:若是字符串包含單引號字符,請考慮使用模板字符串以免必須轉義引號。
普通的字符串文字可能不會跨越多行。
`
在複雜的字符串鏈接上使用模板字符串(分隔),尤爲是涉及多個字符串文字時。模板字符串可能跨越多行。
若是模板字符串跨越多行,則不須要遵循封閉塊的縮進,可是若是添加的空格可有可無。
例:
函數算術(a,b){ return`這是一個算術運算表: $ {a} + $ {b} = $ {a + b} $ {a} - $ {b} = $ {a - b} $ {a} * $ {b} = $ {a * b} $ {a} / $ {b} = $ {a / b}`; }
不要在普通或模板字符串文字中使用行連續(即,在帶有反斜槓的字符串文字中結束一行)。即便ES5容許這樣作,若是任何尾隨空格出如今斜槓以後,它也會致使棘手的錯誤,而且對讀者來講不那麼明顯。
非法:
const longString ='這是一個很是長的字符串,遠遠超過了80 列限制。不幸的是,它包含了很長的空間 如何連續的線條縮進。';
相反,寫
const longString ='這是一個很是長的字符串,遠遠超過80'+ '列限制。它自'+'以來不包含很長的空間 '串聯的字符串更乾淨。';
數字能夠用十進制,十六進制,八進制或二進制指定。使用徹底相同0x
, 0o
和0b
前綴,以小寫字母,十六進制,八進制,二進制和分別。除非緊接着,或x
,不然不要包括前導零 。o
b
使用ES6,語言如今有三種不一樣的for
循環。可是可使用所有for
- of
儘量優先使用循環。
for
- in
循環只能用於dict風格的對象(參見 5.3.3不要混合帶引號和不帶引號的鍵),不該該用於迭代數組。 Object.prototype.hasOwnProperty
應該在for
- in
循環中使用以排除不須要的原型屬性。身高for
- of
和Object.keys
過 for
- in
若是可能的話。
例外是該語言的一個重要部分,應在特殊狀況發生時使用。老是拋出Error
s或子類Error
:永遠不要拋出字符串文字或其餘對象。new
在構建時 老是使用Error
。
自定義異常提供了一種從函數中傳遞其餘錯誤信息的好方法。應該在本機Error
類型不足的任何地方定義和使用它們。
更喜歡在臨時錯誤處理方法上拋出異常(例如傳遞錯誤容器引用類型,或返回具備錯誤屬性的對象)。
在應對捕獲的異常時不執行任何操做是很是正確的。若是真的適合在捕獲區中不採起任何行動,那麼合理的緣由將在評論中解釋。
嘗試{ return handleNumericResponse(response); } catch(ok){ //它不是數字; 那很好,繼續 } return handleTextResponse(response);
非法:
嘗試{ shouldFail(); 失敗('預期錯誤'); } 捕獲(預期){}
提示:與其餘一些語言不一樣,上述模式根本不起做用,由於這會捕獲所引起的錯誤fail
。請assertThrows()
改用。
術語注意:開關塊的大括號內有一個或多個語句組。每一個語句組由一個或多個開關標籤(case FOO:
或者default:
)組成,後跟一個或多個語句。
在switch塊內,每一個語句組會忽然終止(與 break
,return
或throw
ñ除外),或標有註釋,代表執行會,也可能持續到下一個語句組。任何傳達跌倒概念的評論都足夠(一般// fall through
)。在switch塊的最後一個語句組中不須要此特殊註釋。
例:
開關(輸入){ 狀況1: 案例2: prepareOneOrTwo(); //摔倒 案例3: handleOneTwoOrThree(); 打破; 默認: handleLargeNumber(輸入); }
default
案件存在每一個switch語句都包含一個default
語句組,即便它不包含任何代碼。
僅this
在類構造函數和方法中使用,或在類構造函數和方法中定義的箭頭函數中使用。任何其餘用法this
必須@this
在當即封閉函數的JSDoc中聲明。
永遠不要this
用來引用全局對象,事件的上下文,eval
事件的目標或沒必要要的call()
ed或apply()
ed函數。
不要使用with
關鍵字。它使您的代碼更難理解,而且自ES5以來一直在嚴格模式下被禁止。
不要使用eval
或Function(...string)
構造函數(代碼加載器除外)。這些功能具備潛在的危險性,而且在CSP環境中沒法正常工做。
始終使用分號終止語句(函數和類聲明除外,如上所述)。
不要使用非標準功能。這包括已刪除的舊功能(例如WeakMap.clear
),還沒有標準化的新功能(例如,當前的TC39工做草案,任何階段的提案或提議但還沒有完成的Web標準)或專有功能僅在某些瀏覽器中實現。僅使用當前ECMA-262或WHATWG標準中定義的功能。(請注意,針對特定API編寫的項目,例如Chrome擴展或Node.js,顯然可使用這些API)。禁止使用非標準語言「擴展」(例如某些外部轉發器提供的擴展)。
從不使用new
對原始對象包裝(Boolean
,Number
,String
, Symbol
),也不將它們包括在類型註釋。
非法:
const / ** Boolean * / x = new Boolean(false); if(x)alert(typeof x); //提醒'對象' - WAT?
包裝器能夠被稱爲用於強制的函數(優選使用+
或鏈接空字符串)或建立符號。
例:
const / ** boolean * / x = Boolean(0); if(!x)alert(typeof x); //警告'boolean',如預期的那樣
永遠不要經過向構造函數或原型添加方法來修改內置類型。避免依賴於執行此操做的庫。請注意,JSCompiler的運行時庫將盡量提供符合標準的polyfill; 沒有別的能夠修改內置對象。
除非絕對必要,不然不要向全局對象添加符號(例如,第三方API要求)。
標識符僅使用ASCII字母和數字,而且在下面提到的少數狀況下,強調而且很是少(當像Angular這樣的框架須要時)美圓符號。
在合理範圍內儘量給出描述性的名稱。不要擔憂保存水平空間,由於讓新代碼可以當即理解您的代碼更爲重要。不要在項目以外使用對於讀者不明確或不熟悉的縮寫,也不要經過刪除單詞中的字母來縮寫。
priceCountReader //沒有縮寫。 numErrors //「num」是一種廣泛的慣例。 numDnsConnections //大多數人都知道「DNS」表明什麼。
非法:
n //毫無心義 nErr // Ambiguous縮寫。 nCompConns // Ambiguous縮寫。 wgcConnections //只有您的小組知道這表明什麼。 pcReader //不少東西均可以縮寫爲「pc」。 cstmrId //刪除內部字母。 kSecondsPerDay //不要使用匈牙利表示法。
包名都是lowerCamelCase
。例如, my.exampleCode.deepSpace
但不是my.examplecode.deepspace
或my.example_code.deep_space
。
寫入類,接口,記錄和typedef名稱UpperCamelCase
。未經傳播的類只是本地語言:它們沒有標記@private
,所以沒有使用尾隨下劃線命名。
類型名稱一般是名詞或名詞短語。例如,Request
, ImmutableList
,或VisibilityMode
。另外,界面名稱有時多是形容詞或形容詞短語(例如Readable
)。
方法名稱是用lowerCamelCase
。私有方法的名稱必須以尾隨下劃線結尾。
方法名稱一般是動詞或動詞短語。例如,sendMessage
或 stop_
。永遠不須要屬性的getter和setter方法,可是若是使用它們,則應該命名getFoo
(或者可選地isFoo
或者hasFoo
爲布爾值)或者setFoo(value)
用於setter。
下劃線也可能出如今JsUnit測試方法名稱中,以分隔名稱的邏輯組件。test<MethodUnderTest>_<state>
例如,一種典型的模式testPop_emptyStack
。命名測試方法沒有一種正確的方法。
枚舉名稱是寫成的UpperCamelCase
,相似於類,一般應該是單數名詞。枚舉中的單個項目以 CONSTANT_CASE
。
常量名稱使用CONSTANT_CASE
:所有大寫字母,單詞用下劃線分隔。沒有理由使用尾隨下劃線來命名常量,由於私有靜態屬性能夠由(隱式私有)模塊本地替換。
每一個常量都是@const
靜態屬性或模塊本地const
聲明,但並不是全部@const
靜態屬性和模塊本地const
都是常量。在選擇常量狀況以前,請考慮該字段是否真的像一個深不可變的常量。例如,若是該實例的任何可觀察狀態均可以改變,那麼它幾乎確定不是常量。僅僅打算永遠不會改變對象一般是不夠的。
例子:
//常數 const NUMBER = 5; / ** @const * / exports.NAMES = ImmutableList.of('Ed','Ann'); / ** @enum * / exports.SomeEnum = {ENUM_CONSTANT:'value'}; //不是常量 let letVariable ='non-const'; class MyClass {constructor(){/ ** @const * / this.nonStatic ='non-static'; }}; / ** @type {string} * / MyClass.staticButMutable ='not @const,能夠從新分配'; const / ** Set <String> * / mutableCollection = new Set(); const / ** ImmutableSet <SomeMutableType> * / mutableElements = ImmutableSet.of(mutable); const Foo = goog.require('my.Foo'); //鏡像導入的名稱 const logger = log.getLogger('loggers.are.not.immutable');
常數的名稱一般是名詞或名詞短語。
只要它們提升了對徹底限定名稱的可讀性,就應該使用本地別名。遵循與goog.require
s(3.4 goog.require語句)相同的規則,維護別名的最後一部分。別名也能夠在函數內使用。別名必須是const
。
例子:
const staticHelper = importedNamespace.staticHelper; const CONSTANT_NAME = ImportedClass.CONSTANT_NAME; const {assert,assertInstanceof} = asserts;
寫入很是量字段名稱(靜態或其餘)lowerCamelCase
,私有字段的尾部下劃線。
這些名稱一般是名詞或名詞短語。例如,computedValues
或index_
。
參數名稱是寫入的lowerCamelCase
。請注意,即便參數須要構造函數,這也適用。
不該在公共方法中使用單字符參數名稱。
例外:當第三方框架須要時,參數名稱能夠以a開頭$
。此異常不適用於任何其餘標識符(例如,局部變量或屬性)。
lowerCamelCase
如上所述,除了模塊本地(頂級)常量外,寫入局部變量名。函數做用域中的常量仍然以lowerCamelCase
。請注意,即便變量包含構造函數,lowerCamelCase也適用。
模板參數名稱應該是簡潔的單字或單字母標識符,而且必須是所有大寫字母,例如TYPE
或THIS
。
有時將英語短語轉換爲駝峯大小寫的方法不止一種,例如當存在首字母縮略詞或相似IPv6
或 iOS的
異常構造時。爲了提升可預測性,Google Style指定了如下(幾乎)肯定性方案。
從名稱的散文形式開始:
Müller的算法可能會成爲
Muellers算法。
AdWords成爲
廣告詞)。請注意,像
iOS這樣的單詞自己並非真正的駝峯; 它違反任何慣例,所以該建議不適用。
請注意,原始單詞的大小几乎徹底被忽略。
例子:
散文形式 | 正確 | 不正確 |
---|---|---|
XML HTTP請求 |
的XmlHttpRequest | XMLHTTPRequest的 |
新客戶ID |
newCustomerId | newCustomerID |
內秒錶 |
innerStopwatch | innerStopWatch |
在iOS上支持IPv6? |
supportsIpv6OnIos | supportsIPv6OnIOS |
YouTube導入器 |
YouTubeImporter | YoutubeImporter * |
*可接受,但不推薦。
注意:有些單詞在英語中含糊不清:例如,非空
和非空
都是正確的,所以方法名稱checkNonempty和checkNonEmpty一樣都是正確的。
JSDoc用於全部類,字段和方法。
JSDoc塊的基本格式以下例所示:
/ ** *這裏寫了多行JSDoc文本, *正常包裹。 * @param {number} arg要作某事的數字。 * / function doSomething(arg){...}
或者在這個單行示例中:
/ ** @const @private {!Foo}一小段JSDoc。* / this.foo_ = foo;
若是單行註釋溢出到多行,則必須使用多行樣式/**
和*/
它們本身的行。
許多工具從JSDoc註釋中提取元數據以執行代碼驗證和優化。所以,這些評論必須是格式良好的。
JSDoc是用Markdown編寫的,儘管它可能在必要時包含HTML。
請注意,自動提取JSDoc的工具(例如JsDossier)一般會忽略純文本格式,所以若是您這樣作:
/ ** *根據三個因素計算重量: *發送的項目 *收到的物品 *最後一個時間戳 * /
它會像這樣出現:
根據三個因素計算權重:項目發送的項目是上次收到的時間戳
相反,寫一個Markdown列表:
/ ** *根據三個因素計算重量: * - 發送的項目 * - 收到的物品 * - 上次時間戳 * /
Google風格容許使用JSDoc標記的子集。有關完整列表,請參見 9.1 JSDoc標記參考。大多數標籤必須佔據本身的行,標籤位於行的開頭。
非法:
/ ** *「param」標記必須佔用本身的行,不能合併。 * @param {number}離開@param {number}吧 * / 功能添加(左,右){...}
不須要任何附加數據簡單的標記(如@private
, @const
,@final
,@export
)能夠被組合到同一行,具備可選的類型在適當的時候沿。
/ ** *放置更復雜的註釋(如「工具」和「模板」) *在他們本身的路線上。多個簡單標籤(如「導出」和「最終」) *能夠合併爲一行。 * @export @final * @implements {Iterable <TYPE>} * @template TYPE * / class MyClass { / ** * @param {!ObjType} obj一些對象。 * @param {number =} num可選數字。 * / 構造函數(obj,num = 42){ / ** @private @const {!Array <!ObjType | number>} * / this.data_ = [obj,num]; } }
什麼時候組合標籤或以何種順序,可是一致,沒有硬性規則。
有關JavaScript中註釋類型的通常信息,請參閱 爲Closure Compiler註釋JavaScript和Closure Type System中的類型。
換行塊標記縮進四個空格。包裝的描述文本能夠與前面的行描述對齊,但不鼓勵這種水平對齊。
/ ** *說明用於長參數/返回描述的換行。 * @param {string} foo這是一個描述太長而沒法適應的參數 * 一條線。 * @return {number}這會返回描述時間過長的內容 *適合一行。 * / exports.method = function(foo){ 返回5; };
包裝@fileoverview
描述時不要縮進。
文件可能具備頂級文件概述。版權聲明,做者信息和默認可見性級別是可選的。當文件由多個類定義組成時,一般建議使用文件概述。頂級評論旨在將不熟悉代碼的讀者定位到此文件中。若是存在,它能夠提供文件內容的描述以及任何依賴性或兼容性信息。包裹的線條不縮進。
例:
/ ** * @fileoverview文件描述,用途和信息 *關於它的依賴關係。 * @package * /
必須使用描述和任何模板參數,實現的接口,可見性或其餘適當的標記來記錄類,接口和記錄。類描述應該爲讀者提供足夠的信息,以瞭解如何以及什麼時候使用該類,以及正確使用該類所需的任何其餘注意事項。構造函數可能省略了文本描述。@constructor
和@extends
註釋不與class
關鍵字一塊兒使用, 除非該類用於聲明@interface
或擴展泛型類。
/ ** *一個更酷的事件目標,能夠作很酷的事情。 * @implements {Iterable <string>} * / class MyFancyTarget擴展EventTarget { / ** * @param {string} arg1使這更有趣的參數。 * @param {!Array <number>} arg2要處理的數字列表。 * / 構造函數(arg1,arg2){ // ... } }; / ** *記錄也頗有幫助。 * @extends {Iterator <TYPE>} * @record * @template TYPE * / class Listable { / ** @return {TYPE}要返回的行中的下一個項目。* / 下一個() {} }
必須記錄枚舉和typedef。公共枚舉和typedef必須具備非空描述。可使用前一行的JSDoc註釋記錄單個枚舉項。
/ ** *一種有用的類型聯合,常常被重用。 * @typedef {!Bandersnatch |!BandersnatchType} * / 讓CoolUnionType; / ** * bandersnatches的類型。 * @enum {string} * / const BandersnatchType = { / **這種真是太酷了。* / FRUMIOUS:'太酷了', / **不那麼笨拙的那種。* / MANXOME:'manxome', };
Typedef對於定義短記錄類型或聯合,複雜函數或泛型類型的別名很是有用。對於包含許多字段的記錄類型,應避免使用Typedef,由於它們不容許記錄單個字段,也不容許使用模板或遞歸引用。對於大型唱片類型,請選擇@record
。
必須記錄參數和返回類型。this
必要時應記錄該類型。若是方法,參數和返回描述(但不是類型)從方法的其他部分JSDoc或其簽名中顯而易見,則能夠省略它們。方法描述應以用第三人稱聲明語音寫的句子開頭。若是方法覆蓋超類方法,則它必須包含@override
註釋。若是任何類型被細化,則重寫的方法必須包括all @param
和@return
註釋,但若是類型徹底相同則應省略它們。
/ **這是一堂課。* / someClass擴展SomeBaseClass { / ** *在MyClass的一個實例上運行並返回一些東西。 * @param {!MyClass} obj因爲某種緣由須要詳細說明的對象 *跨越多行的解釋。 * @param {!OtherClass} apparentOtherClass * @return {boolean}是否發生了什麼事。 * / someMethod(obj,obviousOtherClass){...} / ** @override * / overriddenMethod(param){...} } / ** *演示頂級函數如何遵循相同的規則。這個 *製做一個數組。 * @param {TYPE} arg * @return {!Array <TYPE>} * @template TYPE * / function makeArray(arg){...}
匿名函數不須要JSDoc,但若是自動類型推斷不足,則能夠內聯指定參數類型。
promise.then( (/ **!Array <number | string> * / items)=> { doSomethingWith(項目); return / ** @type {string} * /(items [0]); });
必須記錄屬性類型。若是名稱和類型提供了足夠的文檔來理解代碼,則能夠省略私有屬性的描述。
公共導出的常量與屬性的註釋方式相同。對於@const
從具備明顯已知類型的表達式初始化的屬性,能夠省略顯式類型。
提示:若是@const
屬性的類型直接來自具備聲明類型的構造函數參數,或者直接來自具備聲明的返回類型的函數調用,則能夠將其視爲「明顯已知」。從更復雜的表達式分配的非const屬性和屬性應該明確聲明它們的類型。
/** 個人課。* / class MyClass { / ** @param {string =} someString * / 構造函數(someString ='default string'){ / ** @private @const * / this.someString_ = someString; / ** @private @const {!OtherType} * / this.someOtherThing_ = functionThatReturnsAThing(); / ** *每一個窗格的最大數量。 * @type {number} * / this.someProperty = 4; } } / ** *放棄以前咱們會嘗試的次數。 * @const * / MyClass.RETRY_COUNT = 33;
類型的註釋中發現@param
,@return
,@this
,和@type
標籤,以及可選的@const
,@export
和任何知名度的標籤。附加到JSDoc標記的類型註釋必須始終用大括號括起來。
類型系統分別定義修飾符!
和?
非null和可空。原始類型(undefined
,string
,number
,boolean
, symbol
,和function(...): ...
),並記錄文字({foo: string, bar: number}
)默認狀況下非空。不要!
爲這些類型添加顯式。對象類型(Array
,Element
,MyClass
,等等)是經過缺省爲空的,但不能從做爲名稱被當即分辨@typedef
「d到一個非空逐默認類型。所以,除了基元和記錄文字以外的全部類型都必須使用?
或明確註釋或!
指示它們是否可爲空。
在類型檢查不能準確推斷表達式類型的狀況下,能夠經過添加類型註釋註釋並將表達式括在括號中來收緊類型。請注意,括號是必需的。
/ ** @type {number} * /(x)
始終指定模板參數。這樣編譯器能夠作得更好,它使讀者更容易理解代碼的做用。
壞:
const / **!Object * / users = {}; const / **!Array * / books = []; const / **!Promise * / response = ...;
好:
const / **!Object <string,!User> * / users = {}; const / **!Array <string> * / books = []; const / **!承諾<!Response> * / response = ...; const / **!Promise <undefined> * / thisPromiseReturnsNothingButParameterIsStillUseful = ...; const / **!Object <string,*> * / mapOfEverything = {};
不該使用模板參數的狀況:
Object
用於類型層次結構而不是相似地圖的結構。可見性註釋(@private
,, )能夠在塊中指定@package
,@protected
也能夠@fileoverview
在任何導出的符號或屬性上指定。不要指定局部變量的可見性,不管是在函數內仍是在模塊的頂層。全部@private
名稱必須如下劃線結尾。
對於任何未經過此規範明確解決的樣式問題,更喜歡執行同一文件中的其餘代碼已經在執行的操做。若是這不能解決問題,請考慮模擬同一包中的其餘文件。
儘量使用項目--warning_level=VERBOSE
。
在作任何事情以前,請確保您徹底理解警告告訴您的內容。若是您不願定爲何會出現警告,請尋求幫助。
理解警告後,請按順序嘗試如下解決方案:
@suppress
註釋。警告在最合理的範圍內被抑制,一般是單個局部變量或很是小的方法。一般,僅爲此緣由提取變量或方法。
例
/ ** @suppress {uselessCode}沒法識別'use asm'聲明* / function fn(){ '使用asm'; 返回0; }
即便是一個類中的大量抑制仍然比將整個類隱藏到這種類型的警告更好。
標記已棄用的方法,類或帶@deprecated
註釋的接口。棄用評論必須包含簡單明瞭的方向,以便人們修復其呼叫站點。
您偶爾會遇到代碼庫中不符合Google風格的文件。這些可能來自收購,或者多是在Google Style在某個問題上佔據一席以前編寫的,或者可能因爲任何其餘緣由而處於非Google風格。
更新現有代碼的樣式時,請遵循如下準則。
全新文件使用Google Style,不管同一包中其餘文件的樣式選擇如何。
將新代碼添加到非Google風格的文件時,建議首先從新格式化現有代碼,但需遵循8.4.1從新格式化現有代碼中的建議 。
若是未執行此從新格式化,則新代碼應儘量與同一文件中的現有代碼保持一致,但不得違反樣式指南。
團隊和項目可能會採用除本文檔以外的其餘樣式規則,但必須接受清理更改可能不遵照這些附加規則,而且不得因違反任何其餘規則而阻止此類清理更改。謹防過多的規則,沒有任何目的。樣式指南並不尋求在每種可能的場景中定義樣式,也不該該。
構建過程生成的源代碼不須要採用Google Style。可是,任何從手寫源代碼引用的生成標識符都必須遵循命名要求。做爲特殊例外,容許此類標識符包含下劃線,這可能有助於避免與手寫標識符衝突。
JSDoc在JavaScript中有多種用途。除了用於生成文檔以外,它還用於控制工具。最着名的是Closure Compiler類型註釋。
Closure編譯器使用的JSDoc文檔在閉包編譯器的註釋JavaScript和閉包類型系統中的類型中進行了描述 。
除了爲Closure Compiler註釋JavaScript中描述的JSDoc以外,如下標記是常見的,而且受到各類文檔生成工具(例如JsDossier)的良好支持,僅用於文檔目的。
標籤 | 模板和示例 | 描述 |
---|---|---|
@author 要麼 @owner |
@author username@google.com (First Last) 例如: / ** * @fileoverview用於處理textareas的實用程序。 * @author kuth@google.com(Uthur Pendragon) * / |
記錄文件的做者或測試的全部者,一般僅在@fileoverview 評論中使用。@owner 單元測試儀表板使用該標記來肯定誰擁有測試結果。 不建議。 |
@bug |
@bug bugnumber 例如: / ** @bug 1234567 * / function testSomething(){ // ... } / ** * @bug 1234568 * @bug 1234569 * / function testTwoBugs(){ // ... } |
指示給定測試函數迴歸測試的錯誤。 多個錯誤應該各自都有本身的 |
@code |
{@code ...} 例如: / ** *移動到選擇中的下一個位置。 *當它出現{@code goog.iter.StopIteration}時 *經過範圍的結束。 * @return {!Node}下一個位置的節點。 * / goog.dom.RangeIterator.prototype.next = function(){ // ... }; |
表示JSDoc描述中的術語是代碼,所以能夠在生成的文檔中正確格式化。 |
@see |
@see Link 例如: / ** *魯莽地添加單個項目。 * @see #addSafely * @see goog.Collect * @see goog.RecklessAdder #add * / |
引用查找到另外一個類函數或方法。 |
@supported |
@supported Description 例如: / ** * @fileoverview事件管理器 *提供抽象的接口 *瀏覽器的事件系統。 * @supported IE10 +,Chrome,Safari * / |
在fileoverview中用於指示文件支持的瀏覽器。 |
@desc |
@desc Message description 例如: / ** @desc通知用戶他們的賬戶已建立。* / exports.MSG_ACCOUNT_CREATED = goog.getMsg( '您的賬戶已經建立成功。'); |
您還能夠在第三方代碼中看到其餘類型的JSDoc註釋。這些註釋出如今JSDoc Toolkit標記參考中,但不被視爲有效Google風格的一部分。
如下注釋特定於特定框架。
骨架 | 標籤 | 文檔 |
---|---|---|
角度1 | @ngInject |
|
聚合物 | @polymerBehavior |
https://github.com/google/closure-compiler/wiki/Polymer-Pass |
如下標記過去是標準的,但如今已棄用。
標籤 | 模板和示例 | 描述 |
---|---|---|
@expose |
@expose |
已過期。不使用。使用@export 和/或@nocollapse 代替。 |
@inheritDoc |
@inheritDoc |
已過期。不使用。請@override 改用。 |
如下是關於Google Style for JavaScript的一些不爲人知或常被誤解的事實的集合。(如下是真實陳述;這不是神話
列表。
)
@author
源文件中既不須要版權聲明也不須要信用。(二者都沒有明確推薦。)強硬的規則來管理如何對類的成員進行排序(5.4類)。
{}
,如(4.1.3空塊:可能簡明)中所述。存在如下工具以支持Google Style的各個方面。
該程序執行類型檢查和其餘檢查,優化和其餘轉換(例如ECMAScript 6到ECMAScript 5代碼下降)。
clang-format
該程序將JavaScript源代碼從新格式化爲Google Style,而且還遵循一些非必需但常常可讀性加強的格式化實踐。
clang-format
不須要。容許做者改變其輸出,容許審稿人要求進行此類更改; 爭議以一般的方式解決。可是,子樹能夠選擇在本地選擇加入。
該程序檢查各類失誤和反模式。
JS一致性框架是一個工具,它是Closure Compiler的一部分,它爲開發人員提供了一種簡單的方法來指定一組額外的檢查,以便在標準檢查之上對其代碼庫運行。例如,一致性檢查能夠禁止訪問某個屬性,或者調用某個函數,或者丟失類型信息(未知數)。
這些規則一般用於強制執行關鍵限制(例如定義可能破壞代碼庫的全局變量)和安全模式(例如使用 eval
或分配innerHTML
),或者更鬆散地提升代碼質量。
有關其餘信息,請參閱JS一致性框架的官方文檔 。
本節描述了當代碼做者沒法使用現代ECMAScript 6語法時要遵循的異常和其餘規則。若是沒法使用ECMAScript 6語法,則須要使用推薦樣式的例外狀況,並在此處概述:
var
容許使用聲明arguments
被容許var
var
聲明不是塊範圍的var
聲明的範圍限定在最近的封閉函數,腳本或模塊的開頭,這可能會致使意外行爲,尤爲是var
在循環內部引用聲明的函數閉包時。如下代碼給出了一個示例:
for(var i = 0; i <3; ++ i){ var iteration = i; setTimeout(function(){console.log(iteration);},i * 1000); } // log 2,2,2 - NOT 0,1,2 //由於`iteration`是函數範圍的,不是循環的本地。
儘管var
聲明的範圍限定在封閉函數的開頭,但var
爲了便於閱讀,聲明應儘量接近它們的首次使用。可是,var
若是在塊外部引用該變量,則不要在塊中放置聲明。例如:
function sillyFunction(){ var count = 0; for(var x in y){ //「count」能夠在這裏聲明,但不要這樣作。 計數++; } console.log(在y'中計算+'項目); }
對於const
將使用關鍵字的全局聲明,若是可用,則var
使用@const 註釋聲明(對於局部變量,這是可選的)。
難道不是這樣作:
if(x){ function foo(){} }
雖然在ECMAScript 6支持功能聲明以前實現的大多數JavaScript VM都沒有標準化。實現彼此不一致,而且與塊範圍函數聲明的如今標準ECMAScript 6行爲不一致。ECMAScript 5和previous僅容許在腳本或函數的根語句列表中進行函數聲明,並在嚴格模式下在塊做用域中明確禁止它們。
要得到一致的行爲,請使用var
帶有函數表達式的初始化來定義塊中的函數:
if(x){ var foo = function(){}; }
goog.provide
/進行依賴管理goog.require
goog.provide
已棄用。goog.module
即便在具備現有goog.provide
用途的項目中,也應使用全部新文件。如下規則僅適用於預先存在的goog.provide文件。
goog.provide
s放在第一位,goog.require
第二位。單獨提供須要空行。goog.provide
和goog.require
陳述。若有必要,可超過80列。截至2016年10月,goog.provide
/ goog.require
dependency管理已棄用。全部新文件,即便在使用goog.provide
舊文件的項目中,也應該使用 goog.module
。
goog.provide
語句應該組合在一塊兒並放在第一位。全部 goog.require
陳述都應遵循。這兩個列表應該用空行分隔。
相似其餘語言的導入語句,goog.provide
而且 goog.require
語句應該寫在一行,即便超過80列線長度限制。
這些行應按字母順序排序,首先是大寫字母:
goog.provide( 'namespace.MyClass'); goog.provide( 'namespace.helperFoo'); goog.require( 'an.extremelyLongNamespace.thatSomeoneThought.wouldBeNice.andNowItIsLonger.Than80Columns'); goog.require( 'goog.dom'); goog.require( 'goog.dom.TagName'); goog.require( 'goog.dom.classes'); goog.require( 'goog.dominoes');
在類上定義的全部成員應該在同一個文件中。只應在包含在同一個類上定義的多個成員的文件中提供頂級類(例如枚舉,內部類等)。
作這個:
goog.provide( 'namespace.MyClass');
不是這個:
goog.provide( 'namespace.MyClass'); goog.provide( 'namespace.MyClass.CONSTANT'); goog.provide( 'namespace.MyClass.Enum'); goog.provide( 'namespace.MyClass.InnerClass'); goog.provide( 'namespace.MyClass.TypeDef'); goog.provide( 'namespace.MyClass.staticMethod');
也能夠提供名稱空間的成員:
goog.provide( 'foo.bar'); goog.provide( 'foo.bar.CONSTANT'); goog.provide( 'foo.bar.method');
goog.scope
goog.scope
已棄用。goog.scope
即便在具備現有goog.scope使用的項目中,也不該使用新文件。
goog.scope
可用於縮短使用goog.provide
/ goog.require
dependency管理的代碼中對命名空間符號的引用。
goog.scope
每一個文件只能添加一次調用。始終將其置於全局範圍內。
開始goog.scope(function() {
調用必須前面只有一個空行,並遵循任何goog.provide
語句,goog.require
語句或頂級註釋。必須在文件的最後一行關閉調用。附加// goog.scope
範圍的結束語。將註釋與分號分隔兩個空格。
與C ++命名空間相似,不要在goog.scope
聲明下縮進。相反,從0列繼續。
僅爲不會從新分配給另外一個對象的名稱(例如,大多數構造函數,枚舉和名稱空間)建立別名。不要這樣作(請參閱下面的如何爲構造函數設置別名):
goog.scope(function(){ var Button = goog.ui.Button; Button = function(){...}; ...
名稱必須與它們是別名的全局的最後一個屬性相同。
goog.provide( 'my.module.SomeType'); goog.require( 'goog.dom'); goog.require( 'goog.ui.Button'); goog.scope(function(){ var Button = goog.ui.Button; var dom = goog.dom; //構造函數聲明後別名新類型。 my.module.SomeType = function(){...}; var SomeType = my.module.SomeType; //像往常同樣在原型上聲明方法: SomeType.prototype.findButton = function(){ //上面有別名的按鈕。 this.button = new Button(dom.getElement('my-button')); }; ... }); // goog.scope