本文档用做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