Google JavaScript样式指南

Google JavaScript样式指南

1简介

本文档用做JavaScript编程语言中Google源代码编码标准完整定义。JavaScript源文件被描述为Google风格,当且仅当它符合此处的规则时。javascript

与其余编程风格指南同样,所涉及的问题不只包括格式化的美学问题,还包括其余类型的约定或编码标准。可是,本文档主要关注咱们广泛遵循的严格规则,并避免提供不明确可执行的建议(不管是经过人工仍是工具)。html

1.1术语说明

在本文件中,除非另有说明:java

  1. 术语注释老是指实现注释。咱们不使用文档注释这一短语,而是使用经常使用术语“JSDoc”来表示人类可读的文本和机器可读的注释 /** … */node

  2. 使用短语时,本样式指南使用RFC 2119术语必须, 不得应该不该该,也能够术语偏好和 避免分别对应应该应该对应命令性和陈述性陈述是规定性的,而且必须符合git

其余术语说明将在整个文件中偶尔出现。es6

1.2指南说明

本文档中的示例代码是非规范性的也就是说,虽然示例是Google风格,但它们可能并无说明表明代码惟一时尚方式。在示例中进行的可选格式选择不得做为规则强制执行。github

2源文件基础知识

2.1文件名

文件名必须所有小写,而且能够包含下划线(_)或短划线(-),但不包含其余标点符号。遵循项目使用的约定。文件名的扩展名必须是.js算法

2.2文件编码:UTF-8

源文件以UTF-8编码shell

2.3特殊字符

2.3.1空白字符

除了行终止符序列以外,ASCII水平空格字符(0x20)是惟一出如今源文件中任何位置的空白字符。这意味着express

  1. 字符串文字中的全部其余空白字符都被转义,而且

  2. 制表符用于缩进。

2.3.2特殊转义序列

对于具备特殊的转义序列(任何字符\'\"\\\b, \f\n\r\t\v),该序列使用,而不是对应的数字逃逸(例如\x0a\u000a\u{a})。传统的八进制转义从未使用过。

2.3.3非ASCII字符

对于剩余的非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字符,所以不要让代码的可读性下降。若是发生这种状况,那些程序就会被破坏,必须修复它们

3源文件结构

源文件按顺序包含

  1. 许可证或版权信息(若是有)
  2. @fileoverview JSDoc,若是存在的话
  3. goog.module 声明
  4. goog.require 声明
  5. 该文件的实现

除了文件的实现以外,正好有一个空行用于分隔存在的每一个部分,其前面可能有1或2个空行。

若是许可或版权信息属于文件,则属于此处。

3.2 @fileoverviewJSDoc,若是存在的话

有关格式规则,请参见7.5顶级/文件级注释

3.3 goog.module声明

全部文件必须goog.module在一行中只声明一个名称:包含goog.module声明的行不得包装,所以是80列限制的例外。

goog.module的整个参数是定义命名空间的。它是包名称(反映代码所在目录结构片断的标识符),以及它定义链接到结尾的主类/枚举/接口。

goog.module( 'search.urlHistory.UrlHistoryService'); 

3.3.1层次结构

模块命名空间可能永远不会被命名为另外一个模块命名空间直接子节点。

非法:

goog.module( 'foo.bar'); //'foo.bar.qux'会好的 goog.module( 'foo.bar.baz');

目录层次结构反映了命名空间层次结构,所以深层嵌套子节点是更高级别父目录的子目录。请注意,这意味着“父”命名空间组的全部者必须知道全部子命名空间,由于它们存在于同一目录中。

3.3.2 goog.setTestOnly

单个goog.module语句能够选择性地跟随对goog.setTestOnly()的调用。

3.3.3 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');)。

3.3.4 ES6模块

不要使用ES6模块(即exportimport关键字),由于它们的语义还没有最终肯定。请注意,一旦语义彻底标准化,将从新审视此策略。

3.4 goog.require陈述

使用goog.require语句完成导入,在模块声明后当即组合在一块儿。每一个都goog.require被分配给一个常量别名,或者被解构为几个常量别名。这些别名是引用required依赖关系的惟一可接受的方式,不管是在代码中仍是在类型注释中:除非做为参数,不然永远不会使用彻底限定名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'); //必须在顶级 // ... }

3.4.1 goog.forwardDeclare

goog.forwardDeclare不常常须要,但它是打破循环依赖或引用后期加载代码的有用工具。这些陈述组合在一块儿,并紧跟任何goog.require陈述。一个 goog.forwardDeclare语句必须遵循相同的样式规则的 goog.require声明。

3.5文件的实现

在声明全部依赖性信息以后(由至少一个空行分隔)以后的实际实现。

这可能包括任何模块本地声明(常量,变量,类,函数等),以及任何导出的符号。

4格式化

术语注意相似块的构造是指类,函数,方法或大括号分隔的代码块的主体。请注意,经过 5.2数组文字5.3对象文字,能够选择将任何数组或对象文字视为相似于块的结构。

提示:使用clang-formatJavaScript社区已投入精力确保clang格式在JavaScript文件上作正确的事情clang-format与几位受欢迎的编辑集成。

4.1大括号

4.1.1支架用于全部控制结构

大括号都须要全部的控制结构(即ifelsefordo, while,以及任何其余),即便身体只包含一个声明。非空块的第一个语句必须从它本身的行开始。

非法:

if(someVeryLongCondition()) 作一点事();
 for(let i = 0; i <foo.length; i ++)bar(foo [i]);

例外:一个简单的if语句能够彻底放在一行而没有包装(而且没有else)能够保留在一行中,当它提升可读性时没有括号。这是控制结构能够省略大括号和换行符的惟一状况。

if(shortCondition())返回; 

4.1.2非空块:K&R风格

大括号遵循Kernighan和Ritchie风格(埃及括号),用于 非空块和块状结构:

  • 开口支架前没有断线。
  • 开口支撑后换行。
  • 关闭括号前的换行符。
  • 若是该大括号终止语句或函数或类语句的主体或类方法,在大括号后的换行符具体而言,有没有支架后换行符若是随后elsecatch, while,或逗号,分号,或右括号。

例:

class InnerClass { 构造函数(){}
 / ** @param {number} foo * / method(foo){ if(condition(foo)){ 尝试{ //注意:这可能会失败。 东西(); } catch(err){ 恢复(); } } } }

4.1.3空块:可能简洁

空块或块状结构能够在打开后当即关闭,其间没有字符,空格或换行符(即{}), 除非它是多块语句的一部分(直接包含多个块的语句)ifelsetrycatchfinally)。

例:

function doNothing(){} 

非法:

if(condition){ // ... } else if(otherCondition){} else { // ... }
 尝试{ // ... } catch(e){}

4.2块缩进:+2个空格

每次打开新的块或块状构造时,缩进都会增长两个空格。当块结束时,缩进返回到先前的缩进级别。缩进级别适用于整个块中的代码和注释。(参见4.1.2非空块中的示例:K&R样式)。

4.2.1数组文字:可选择块状

任何数组文字均可以选择格式化,就好像它是一个“块状结构”。例如,如下都是有效的(不是详尽的列表):

const a = [ 0, 1, 2, ]。
 const b = [0,1,2];

const c = [0,1,2];
 someMethod(foo,[ 0,1,2, ],酒吧);

容许其余组合,特别是在强调元素之间的语义分组时,但不该仅用于减小较大数组的垂直大小。

4.2.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, },酒吧);

4.2.3类文字

类文字(不管是声明仍是表达式)缩进为块。不要在方法以后添加分号,或者在类声明的右括号以后添加分号 (包含类表达式的赋值 - 如赋值 - 仍以分号结尾)。除非类扩展了模板化类型,不然请使用extends关键字,而不是 @extendsJSDoc注释。

例:

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(消息){} }

4.2.4函数表达式

在函数调用的参数列表中声明匿名函数时,函数体将比前一个缩进深度缩进两个空格。

例:

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(); } });

4.2.5切换语句

与任何其余块同样,开关块的内容缩进为+2。

在切换标签以后,出现换行符,而且缩进级别增长+2,就像打开一个块同样。若是词法做用域须要,可使用显式块。如下开关标签返回到先前的缩进级别,就像块已关闭同样。

break和如下状况之间的空行是可选的

例:

开关(动物){ case Animal.BANDERSNATCH: handleBandersnatch(); 打破;
 case Animal.JABBERWOCK: handleJabberwock(); 打破;
 默认: 抛出新错误('未知动物'); }

4.3声明

4.3.1每行一个语句

每一个语句后面都有一个换行符。

4.3.2分号是必需的

每一个语句必须以分号结束。禁止依赖自动分号插入。

4.4柱限:80

JavaScript代码的列限制为80个字符。除非另有说明,不然任何超出此限制的行都必须换行,如 4.5换行中所述

例外:

  1. 不能遵照列限制的行(例如,JSDoc中的长URL或要复制和粘贴的shell命令)。
  2. goog.modulegoog.require语句(参见3.3 goog.module语句和 3.4 goog.require语句)。

4.5换行

术语注意换行定义为将单个表达式分红多行。

有没有表现出全面的,肯定性的公式究竟如何线路缠绕在每一种状况。一般有几种有效的方法来包装同一段代码。

注意:虽然换行的典型缘由是为了不溢出列限制,但即便是实际符合列限制的代码也可能由做者自行决定是否换行。

提示:提取方法或局部变量能够解决问题,而无需换行。

4.5.1在哪里打破

换行的主要指令是:更喜欢在更高的句法层面打破

首选:

currentEstimate = calc(currentEstimate + x * currentEstimate)/ 2.0F;

灰心:

currentEstimate = calc(currentEstimate + x * currentEstimate)/ 2.0f;

在前面的例子中,从最高到最低的句法级别以下:赋值,除法,函数调用,参数,数字常量。

运算符包含以下:

  1. 当一条线在操做员处断开时,断点出如今符号以后。(请注意,这与Google风格的Java风格不一样。)
    1. 这不适用于dot.),它实际上不是运算符。
  2. 方法或构造函数名称保持附加到其后面的左括号(()。
  3. 逗号(,)保持附加到其前面的标记。

注意:换行的主要目标是拥有清晰的代码,而不必定是适合最小行数的代码。

4.5.2缩进连续行至少+4个空格

当换行时,第一行(每一个延续行以后的每一从原始行缩进至少+4,除非它属于块缩进的规则。

当存在多个连续线时,压痕能够适当地变化超过+4。一般,更深层次的句法级别的连续行用4的较大倍数缩进,当且仅当它们以语法并行元素开头时,两行使用相同的缩进级别。

4.6.3水平对齐:不鼓励使用可变数量的空格来阻止某些令牌与前一行对齐。

4.6空白

4.6.1垂直空白

出现一个空白行:

  1. 在类或对象文字中的连续方法之间
    1. 例外:对象文字中两个连续属性定义之间的空行(它们之间没有其余代码)是可选的。根据须要使用这样的空行来建立字段的逻辑分组
  2. 在方法体内,谨慎地建立语句的逻辑分组不容许在函数体的开头或结尾处出现空行。
  3. 可选地在类或对象文字中的第一个或最后一个方法以前(既不鼓励也不鼓励)。
  4. 按照本文档其余部分的要求(例如 3.4 goog.require语句)。

容许多个连续的空白行,但从不须要(也不鼓励)。

4.6.2水平空格

水平空白的使用取决于位置,并分为三大类:前导(在行的开头),尾随(在行的末尾)和内部领先的空白(即缩进)在别处获得解决。禁止使用尾随空格。

除了语言或其余样式规则所要求的内容以外,除了文字,注释和JSDoc以外,单个内部ASCII空间也出如今如下位置

  1. 分离任何保留字(如ifforcatch)从一个开括号((它后面在该行)。
  2. 将任何保留字(例如elsecatch)与}该行前面的右大括号()分开。
  3. 在任何打开大括号({)以前,有两个例外:
    1. 在做为函数的第一个参数或数组文字中的第一个元素的对象文字以前(例如foo({a: [{c: d}]}))。
    2. 在模板扩展中,由于它被语言禁止(例如abc${1 + 2}def)。
  4. 在任何二元或三元运算符的两边。
  5. 在逗号(,)或分号(;)以后。请注意,在这些字符以前毫不容许使用空格
  6. :对象文字中冒号(以后
  7. 在双斜杠(//)的两侧开始一行结束注释。这里容许多个空格,但不是必需的。
  8. 在打开JSDoc注释字符后和关闭字符的两边(例如,对于短格式类型声明或强制转换:this.foo = /** @type {number} */ (bar);function(/** string */ foo) {)。

4.6.3水平对齐:不鼓励

术语注意水平对齐是在代码中添加可变数量的附加空格的作法,目的是使某些标记直接出如今前一行的某些其余标记下方。

这种作法是容许的,但谷歌风格一般不鼓励这种作法甚至不须要在已经使用过的地方保持水平对齐。

这是一个没有对齐的示例,后面跟一个对齐。二者都是容许的,但后者是不鼓励的:

{ 微小的:42,//这很棒 更长:435,//这也是 };
 { 微小:42,//容许,但将来的编辑 更长:435,//可能会使它不对齐 };

提示:对齐能够提升可读性,但会为未来的维护带来问题。考虑一下须要触及一条线的将来变化。这种改变可能会使之前使人愉悦的格式化变得严重,而且这是容许的。更常见的是,它会提示编码人员(也许你)调整附近行的空白,这可能会引起一连串的从新格式化。那个单线变化如今有一个爆炸半径。这可能会致使无心义的忙碌工做,但最多它仍然会破坏版本历史信息,减慢审阅者的速度并加重合并冲突。

4.6.4函数参数

但愿将全部函数参数放在与函数名称相同的行上。若是这样作会超过80列限制,则参数必须以可读的方式换行。为了节省空间,您能够尽量接近80,或者将每一个参数放在本身的行上以加强可读性。缩进应该是四个空格。容许对齐括号,但不鼓励。如下是参数包装最多见的模式:

//参数从一个新行开始,缩进四个空格。首选的时候 //参数与函数名称(或关键字)不在同一行 //“功能”)但彻底适合第二行。工做时间很长 //函数名称,无需从新定义便可重命名,空间不足。 作一点事( descriptiveArgumentOne,descriptiveArgumentTwo,descriptiveArgumentThree){ // ... }
 //若是参数列表较长,请换行80.使用较少的垂直空间, //但违反了矩形规则,所以不推荐使用。 doSomething(veryDescriptiveArgumentNumberOne,veryDescriptiveArgumentTwo, tableModelEventHandlerProxy,artichokeDescriptorAdapterIterator){ // ... }
 //四个空格,每行一个参数。使用长函数名称, //幸存重命名,并强调每一个参数。 作一点事( veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator){ // ... }

4.7分组括号:推荐

只有看成者和审阅者赞成没有合理的机会在没有它们的状况下错误解释代码时,才会省略可选的分组括号,也不会使代码更易于阅读。它是不是 合理的假设,每一个读者有记忆的整个运算符优先级表。

如下不要使用没必要要的括号整个表达式 deletetypeofvoidreturnthrowcaseinof,或yield

类型转换须要括号:/** @type {!Foo} */ (foo)

4.8评论

本节介绍实现注释JSDoc在7 JSDoc中单独解决

4.8.1阻止评论风格

块注释与周围代码的缩进级别相同。他们多是/* … */或者是//风格。对于多行/* … */注释,后续行必须以*与*前一行的对齐开头,以使注释显而易见,没有额外的上下文。只要值和方法名称没有充分表达含义,“参数名称”注释就应出如今值以后。

/ * * 这是 * 好的。 * /
 // 因此 // 这是。
 / *这也很好。* /
 someFunction(obviousParam,true / * shouldRender * /,'hello'/ * name * /);

注释不包含在用星号或其余字符绘制的框中。

不要将JSDoc(/** … */)用于上述任何实现注释。

5语言功能

JavaScript包含许多可疑(甚至危险)的功能。本节描述了可能使用或未使用的功能,以及对其使用的任何其余限制。

5.1局部变量声明

5.1.1使用constlet

声明全部的局部变量有两种constlet除非须要从新分配变量,不然默认使用const。var 不得使用关键字。

5.1.2每一个声明一个变量

每一个局部变量声明只声明一个变量:声明,例如let a = 1, b = 2;未使用。

5.1.3在须要时声明,尽快初始化

局部变量不能习惯性地在其包含块或块状结构的开始申报。相反,局部变量被声明为接近它们首次使用的点(在合理范围内),以最小化它们的范围。

5.1.4根据须要声明类型

能够在声明上方的行上添加JSDoc类型注释,或者在变量名称以前添加内联。

例:

const / **!Array <number> * / data = [];
 / ** @type {!Array <number>} * / const data = [];

提示:在许多状况下,编译器能够推断模板化类型而不是其参数。当初始化文字或构造函数调用不包含模板参数类型的任何值(例如,空数组,对象,Maps或Sets),或者在闭包中修改变量时,尤为如此在这些状况下,局部变量类型注释特别有用,不然编译器会将模板参数推断为未知。

5.2数组文字

5.2.1使用尾随逗号

每当最终元素和结束括号之间存在换行符时,请包含尾随逗号。

例:

const值= [ '第一价值', '第二价值', ]。

5.2.2不要使用可变参数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是一个整数而后 a3x1全部元素都是大小的数组undefined若是x1是任何其余数字,那么将抛出异常,若是它是其余任何东西,那么它将是单元素数组。

相反,写

const a1 = [x1,x2,x3]; const a2 = [x1,x2]; const a3 = [x1]; const a4 = [];

new Array(length)适当时容许使用显式分配给定长度的数组

5.2.3非数字属性

不要在数组上定义或使用非数字属性(除了 length)。使用Map(或Object)代替。

5.2.4解构

能够在赋值的左侧使用数组文字来执行解构(例如,从单个数组或可迭代解包多个值时)。能够包括最终的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)将多个值打包到函数的参数或返回中,尽量将对象解构更改成数组解构,由于它容许命名单个元素并为每一个元素指定不一样的类型。*

5.2.5传播运营商

数组文字能够包括扩展运算符(...)以展平一个或多个其余迭代中的元素。应该使用扩展运算符而不是更笨拙的构造Array.prototype以后没有空间 ...

例:

[... foo] //首选Array.prototype.slice.call(foo) [... foo,... bar] //首选foo.concat(bar)

5.3对象文字

5.3.1使用尾随逗号

每当最终属性和右大括号之间存在换行符时,请包含尾随逗号。

5.3.2不要使用Object构造函数

虽然Object没有一样的问题Array,但仍然不容许一致性。请改用对象文字({}{a: 0, b: 1, c: 2})。

5.3.3不要混合引用和不引用的密钥

对象文字能够表示结构(带有不带引号的键和/或符号)或dicts(带引号和/或计算键)。不要在单个对象文字中混合使用这些键类型。

非法:

{ a:42,// struct-style unquoted key 'b':43,// dict-style引用键 }

5.3.4计算属性名称

{['key' + foo()]: 42}容许使用计算属性名称(例如),而且将其视为dict样式(引用)键(即,不得与非引用键混合),除非计算属性是符号(例如,[Symbol.iterator])。枚举值也可用于计算键,但不该与同一文字中的非枚举键混合使用。

5.3.5方法速记

可使用方法shorthand({method() {… }})代替冒号后面紧跟一个function或箭头函数文字,在对象文字上定义方法

例:

返回{ 东西:'糖果', 方法() { 归还这个。//返回'candy' }, };

请注意,this在方法简写中或function引用对象文字自己,而this在箭头函数中引用对象文字以外的范围。

例:

class { getObjectLiteral(){ this.stuff ='fruit'; 返回{ 东西:'糖果', 方法:()=> this.stuff,//返回'fruit' }; } }

5.3.6速记属性

对象文字容许使用速记属性。

例:

const foo = 1; const bar = 2; const obj = { FOO, 酒吧, method(){return this.foo + this.bar; }, }; assertEquals(3,obj.method());

5.3.7解构

能够在赋值的左侧使用对象解构模式来执行解构并从单个对象解包多个值。

结构化对象也能够用做函数参数,但应尽量简单:单个级别的不带引号的速记属性。嵌套和计算属性的更深层次可能不会用于参数解构。在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语句)。

5.3.8枚举

经过将@enum注释添加到对象文字来定义枚举定义后,可能没法将其余属性添加到枚举中。枚举必须是常量,全部枚举值必须是不可变的。

/ ** *支持的温标。 * @enum {string} * / const TemperatureScale = { CELSIUS:'摄氏', FAHRENHEIT:'华氏', };
 / ** *枚举有两个选项。 * @enum {number} * / const Option = { / **使用的选项应该是第一个。* / FIRST_OPTION:1, / **两个选项中的第二个。* / SECOND_OPTION:2, };

5.4课程

5.4.1构造函数

具体类的构造函数是可选的。子类构造函数必须super()在设置任何字段或以其余方式访问以前调用 this接口不能定义构造函数。

5.4.2字段

在构造函数中设置全部具体对象的字段(即除方法以外的全部属性)。注释永远不会从新分配的字段@const (这些字段没必要很是不可变)。必须使用私有字段进行注释, @private而且其名称必须以尾随下划线结尾。字段永远不会设置在具体类上prototype

例:

class Foo { constructor(){ / ** @private @const {!Bar} * / this.bar_ = computeBar(); } }

提示:在构造函数完成后,毫不应将属性添加到实例或从实例中删除属性,由于它会显着阻碍VM的优化能力。若有必要,稍后初始化的字段应undefined在构造函数中显式设置为以防止之后的形状更改。添加 @struct到对象将检查未添加/访问未声明的属性。默认状况下,类会添加此类。

5.4.3计算属性

当属性是符号时,计算属性只能在类中使用。不容许使用Dict样式属性(即,引用或计算的非符号键,如5.3.3中所定义,不混合引用和不带引号的键)。[Symbol.iterator]应该为逻辑上可迭代的任何类定义一个 方法。除此以外,Symbol应谨慎使用。

提示:请当心使用任何其余内置符号(例如Symbol.isConcatSpreadable),由于它们不是由编译器填充的,所以不适用于旧版浏览器。

5.4.4静态方法

在不干扰可读性的地方,更喜欢模块本地函数而不是私有静态方法。

静态方法只应在基类自己上调用。不该该在包含动态实例的变量上调用静态方法,动态实例能够是构造函数或子类构造函数(@nocollapse若是已完成必须定义 ),而且不能直接在未定义方法的子类上调用自己。

非法:

class Base {/ ** @nocollapse * / static foo(){}} class Sub扩展Base {} function callFoo(cls){cls.foo(); } //劝阻:不要动态调用静态方法 Sub.foo(); //非法:不要在没有本身定义它的子类上调用静态方法

5.4.5旧式类声明

虽然ES6类是首选,但有些状况下ES6类可能不可行。例如:

  1. 若是存在或将存在子类(包括建立子类的框架),则没法当即将其更改成使用ES6类语法。若是这样的类使用ES6语法,则须要修改全部不使用ES6类语法的下游子类。

  2. this在调用超类构造函数以前须要已知值的框架,由于具备ES6超类的构造函数this在调用super返回以前无权访问实例

在全部其余方面的风格指南仍然适用于这样的代码:letconst,默认参数,休息和箭头的功能都应该用在适当的时候。

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库

5.4.6不要prototype直接操纵s

class与定义prototype属性相比,关键字容许更清晰,更易读的类定义普通的实现代码没有业务操做这些对象,尽管它们仍然有用于定义5.4.5旧式类声明中定义的@record 接口和类Mixins和修改内置对象的原型是明确禁止的。

例外:框架代码(如Polymer或Angular)可能须要使用prototypes,而且不该采用更差的解决方法来避免这样作。

例外:在接口中定义字段(参见5.4.9接口)。

5.4.7吸气剂和固定剂

不要使用JavaScript getter和setter属性它们可能使人惊讶且难以推理,而且在编译器中的支持有限。改成提供普通方法。

例外:使用数据绑定框架(例如Angular和Polymer)时,可能会谨慎使用getter和setter。但请注意,编译器支持是有限的。使用它们时,必须使用get foo()set foo(value)在类或对象文字中定义它们,或者若是不可能,则使用Object.defineProperties不要使用 Object.defineProperty,这会干扰属性重命名。吸气剂 不得改变可观察状态。

非法:

class Foo { get next(){return this.nextId ++; } }

5.4.8覆盖toString

toString方法可能被覆盖,但必须始终成功,而且永远不会有可见的反作用。

提示:特别要注意从toString调用其余方法,由于异常条件可能致使无限循环。

5.4.9接口

接口能够用@interface声明@record声明的接口@record能够@implements由类或对象文字显式(即经过)或隐式实现。

接口上的全部非静态方法体必须是空块。必须在接口体以后定义字段做为存根prototype

例:

/ ** *可使用的东西。 * @record * / class Frobnicator { / ** *根据给定的策略执行frobnication。 * @param {!FrobnicationStrategy}策略 * / frobnicate(策略){} }
 / ** @type {number}放弃以前的尝试次数。* / Frobnicator.prototype.attempts;

5.5功能

5.5.1顶级功能

导出的函数能够直接在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)=> { //作一些事...... };

5.5.2嵌套函数和闭包

函数可能包含嵌套函数定义。若是为函数指定名称颇有用,则应将其分配给本地const

5.5.3箭头功能

箭头函数提供了简洁的语法并修复了许多困难 this首选箭头函数优先于function关键字,特别是嵌套函数(但请参见5.3.5方法简写)。

喜欢使用箭头功能f.bind(this),特别是结束 goog.bind(f, this)避免写做const self = this箭头函数对于回调特别有用,回调有时会传递意外的附加参数。

箭头的右侧能够是单个表达式或块。若是只有一个非破坏参数,则参数周围的括号是可选的。

提示:即便对于单参数箭头也使用括号是一个好习惯,由于若是在添加其余参数时忘记括号,代码仍然能够合理地解析(但不正确)。

5.5.4发电机

生成器启用了许多有用的抽象,能够根据须要使用。

定义生成器函数时,在存在时*function关键字附加关键字,并使用函数名称中的空格将其分隔。当使用委托收益,附加*yield关键字。

例:

/ ** @return {!Iterator <number>} * / function * gen1(){ 产量42; }
 / ** @return {!Iterator <number>} * / const gen2 = function *(){ yield * gen1(); }
 class SomeClass { / ** @return {!Iterator <number>} * / * gen(){ 产量42; } }

5.5.5参数

必须在函数定义以前的JSDoc中使用JSDoc注释键入函数参数,但在相同签名@override的状况下除外,其中省略全部类型。

参数类型能够在参数名称以前的内联中指定(如(/** number */ foo, /** string */ bar) => foo + bar)。内联和 @param类型注释不能在同一个函数定义中混合使用。

5.5.5.1默认参数

使用参数列表中的equals运算符容许使用可选参数。可选参数必须包含equals运算符两侧的空格,其名称与所需参数彻底相同(即不带前缀 opt_),=在JSDoc类型中使用后缀,在必需参数以后使用,而不使用产生可观察反作用的初始值设定项。全部可选参数必须在函数声明中具备默认值,即便该值为undefined

例:

/ ** * @param {string}必需此参数始终是必需的。 * @param {string =} optional此参数能够省略。 * @param {!Node =} node另外一个可选参数。 * / 函数maybeDoSomething(必需,可选='',node = undefined){}

谨慎使用默认参数。当有少数几个可选参数没有天然顺序时,首选解构(如在5.3.7解构中 )以建立可读API。

注意:与Python的默认参数不一样,可使用返回新的可变对象(例如{}[])的初始化器,由于每次使用默认值时都会对初始化程序进行求值,所以不会在调用之间共享单个对象。

提示:虽然包含函数调用的任意表达式能够用做初始化程序,但这些表达式应尽量简单。避免暴露共享可变状态的初始化器,由于这很容易在函数调用之间引入意外耦合。

5.5.5.2休息参数

使用rest参数而不是访问argumentsRest参数...在其JSDoc中使用前缀键入rest参数必须是列表中的最后一个参数。...参数名称和参数名称之间没有空格不要命名rest参数var_args永远不要命名局部变量或参数arguments,这会混淆内置名称。

例:

/ ** * @param {!Array <string>}数组这是一个普通的参数。 * @param {... number}数字参数的其他部分都是数字。 * / function variadic(array,... numbers){}

5.5.6退货

函数返回类型必须在函数定义正上方的JSDoc中指定,除非在相同签名@override状况下,全部类型都被省略。

5.5.7泛型

必要时@template TYPE在类定义上方的JSDoc中声明泛型函数和方法

5.5.8传播运营商

函数调用可使用spread运算符(...)。Function.prototype.apply当数组或iterable被解包为可变函数的多个参数时,首选扩展运算符以后没有空间...

例:

function myFunction(... elements){} myFunction(... array,... iterable,... generator());

5.6字符串文字

5.6.1使用单引号

普通字符串文字用单引号('分隔,而不是双引号(")。

提示:若是字符串包含单引号字符,请考虑使用模板字符串以免必须转义引号。

普通的字符串文字可能不会跨越多行。

5.6.2模板字符串

`在复杂的字符串链接上使用模板字符串(分隔),尤为是涉及多个字符串文字时。模板字符串可能跨越多行。

若是模板字符串跨越多行,则不须要遵循封闭块的缩进,可是若是添加的空格可有可无。

例:

函数算术(a,b){ return`这是一个算术运算表: $ {a} + $ {b} = $ {a + b} $ {a} - $ {b} = $ {a - b} $ {a} * $ {b} = $ {a * b} $ {a} / $ {b} = $ {a / b}`; }

5.6.3没有续行

不要在普通或模板字符串文字中使用行连续(即,在带有反斜杠的字符串文字中结束一行)。即便ES5容许这样作,若是任何尾随空格出如今斜杠以后,它也会致使棘手的错误,而且对读者来讲不那么明显。

非法:

const longString ='这是一个很是长的字符串,远远超过了80 列限制。不幸的是,它包含了很长的空间 如何连续的线条缩进。';

相反,写

const longString ='这是一个很是长的字符串,远远超过80'+ '列限制。它自'+'以来不包含很长的空间 '串联的字符串更干净。';

5.7数字文字

数字能够用十进制,十六进制,八进制或二进制指定。使用彻底相同0x, 0o0b前缀,以小写字母,十六进制,八进制,二进制和分别。除非紧接着,或x不然不要包括前导零 ob

5.8控制结构

5.8.1 For循环

使用ES6,语言如今有三种不一样的for循环。可是可使用所有forof尽量优先使用循环。

forin循环只能用于dict风格的对象(参见 5.3.3不要混合带引号和不带引号的键),不该该用于迭代数组。 Object.prototype.hasOwnProperty应该在forin循环中使用以排除不须要的原型属性。身高forofObject.keys过 forin若是可能的话。

5.8.2例外状况

例外是该语言的一个重要部分,应在特殊状况发生时使用。老是抛出Errors或子类Error:永远不要抛出字符串文字或其余对象。new在构建时 老是使用Error

自定义异常提供了一种从函数中传递其余错误信息的好方法。应该在本机Error 类型不足的任何地方定义和使用它们

更喜欢在临时错误处理方法上抛出异常(例如传递错误容器引用类型,或返回具备错误属性的对象)。

5.8.2.1空捕获块

在应对捕获的异常时不执行任何操做是很是正确的。若是真的适合在捕获区中不采起任何行动,那么合理的缘由将在评论中解释。

尝试{ return handleNumericResponse(response); } catch(ok){ //它不是数字; 那很好,继续 } return handleTextResponse(response);

非法:

 尝试{ shouldFail(); 失败('预期错误'); } 捕获(预期){}

提示:与其余一些语言不一样,上述模式根本不起做用,由于这会捕获所引起的错误failassertThrows()改用。

5.8.3切换语句

术语注意:开关块的大括号内有一个或多个语句组。每一个语句组由一个或多个开关标签(case FOO:或者default:)组成,后跟一个或多个语句。

5.8.3.1跌倒:评论

在switch块内,每一个语句组会忽然终止(与 breakreturnthrowñ除外),或标有注释,代表执行会,也可能持续到下一个语句组。任何传达跌倒概念的评论都足够(一般// fall through)。在switch块的最后一个语句组中不须要此特殊注释。

例:

开关(输入){ 状况1: 案例2: prepareOneOrTwo(); //摔倒 案例3: handleOneTwoOrThree(); 打破; 默认: handleLargeNumber(输入); }
5.8.3.2 default案件存在

每一个switch语句都包含一个default语句组,即便它不包含任何代码。

5.9这个

this在类构造函数和方法中使用,或在类构造函数和方法中定义的箭头函数中使用。任何其余用法this必须@this在当即封闭函数的JSDoc中声明。

永远不要this用来引用全局对象,事件的上下文,eval事件的目标或没必要要的call()ed或apply()ed函数。

5.10不容许的功能

5.10.1 with

不要使用with关键字。它使您的代码更难理解,而且自ES5以来一直在严格模式下被禁止。

5.10.2动态代码评估

不要使用evalFunction(...string)构造函数(代码加载器除外)。这些功能具备潜在的危险性,而且在CSP环境中没法正常工做。

5.10.3自动分号插入

始终使用分号终止语句(函数和类声明除外,如上所述)。

5.10.4非标准功能

不要使用非标准功能。这包括已删除的旧功能(例如WeakMap.clear),还没有标准化的新功能(例如,当前的TC39工做草案,任何阶段的提案或提议但还没有完成的Web标准)或专有功能仅在某些浏览器中实现。仅使用当前ECMA-262或WHATWG标准中定义的功能。(请注意,针对特定API编写的项目,例如Chrome扩展或Node.js,显然可使用这些API)。禁止使用非标准语言“扩展”(例如某些外部转发器提供的扩展)。

5.10.5原始类型的包装器对象

从不使用new对原始对象包装(BooleanNumberString, Symbol),也不将它们包括在类型注释。

非法:

const / ** Boolean * / x = new Boolean(false); if(x)alert(typeof x); //提醒'对象' - WAT?

包装器能够被称为用于强制的函数(优选使用+或链接空字符串)或建立符号。

例:

const / ** boolean * / x = Boolean(0); if(!x)alert(typeof x); //警告'boolean',如预期的那样

5.10.6修改内置对象

永远不要经过向构造函数或原型添加方法来修改内置类型。避免依赖于执行此操做的库。请注意,JSCompiler的运行时库将尽量提供符合标准的polyfill; 没有别的能够修改内置对象。

除非绝对必要,不然不要向全局对象添加符号(例如,第三方API要求)。

6命名

6.1全部标识符共有的规则

标识符仅使用ASCII字母和数字,而且在下面提到的少数状况下,强调而且很是少(当像Angular这样的框架须要时)美圆符号。

在合理范围内尽量给出描述性的名称。不要担忧保存水平空间,由于让新代码可以当即理解您的代码更为重要。不要在项目以外使用对于读者不明确或不熟悉的缩写,也不要经过删除单词中的字母来缩写。

priceCountReader //没有缩写。 numErrors //“num”是一种广泛的惯例。 numDnsConnections //大多数人都知道“DNS”表明什么。

非法:

n //毫无心义 nErr // Ambiguous缩写。 nCompConns // Ambiguous缩写。 wgcConnections //只有您的小组知道这表明什么。 pcReader //不少东西均可以缩写为“pc”。 cstmrId //删除内部字母。 kSecondsPerDay //不要使用匈牙利表示法。

6.2按标识符类型的规则

6.2.1包名称

包名都是lowerCamelCase例如, my.exampleCode.deepSpace但不是my.examplecode.deepspacemy.example_code.deep_space

6.2.2班级名称

写入类,接口,记录和typedef名称UpperCamelCase未经传播的类只是本地语言:它们没有标记@private,所以没有使用尾随下划线命名。

类型名称一般是名词或名词短语。例如,Request, ImmutableList,或VisibilityMode另外,界面名称有时多是形容词或形容词短语(例如Readable)。

6.2.3方法名称

方法名称是用lowerCamelCase私有方法的名称必须以尾随下划线结尾。

方法名称一般是动词或动词短语。例如,sendMessage或 stop_永远不须要属性的getter和setter方法,可是若是使用它们,则应该命名getFoo(或者可选地isFoo或者hasFoo 为布尔值)或者setFoo(value)用于setter。

下划线也可能出如今JsUnit测试方法名称中,以分隔名称的逻辑组件。test<MethodUnderTest>_<state>例如,一种典型的模式testPop_emptyStack命名测试方法没有一种正确的方法。

6.2.4枚举名称

枚举名称是写成的UpperCamelCase,相似于类,一般应该是单数名词。枚举中的单个项目以 CONSTANT_CASE

6.2.5常量名称

常量名称使用CONSTANT_CASE:所有大写字母,单词用下划线分隔。没有理由使用尾随下划线来命名常量,由于私有静态属性能够由(隐式私有)模块本地替换。

6.2.5.1“常数”的定义

每一个常量都是@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');

常数的名称一般是名词或名词短语。

6.2.5.1本地别名

只要它们提升了对彻底限定名称的可读性,就应该使用本地别名。遵循与goog.requires(3.4 goog.require语句相同的规则,维护别名的最后一部分。别名也能够在函数内使用。别名必须是const

例子:

const staticHelper = importedNamespace.staticHelper; const CONSTANT_NAME = ImportedClass.CONSTANT_NAME; const {assert,assertInstanceof} = asserts;

6.2.6很是量字段名称

写入很是量字段名称(静态或其余)lowerCamelCase,私有字段的尾部下划线。

这些名称一般是名词或名词短语。例如,computedValues 或index_

6.2.7参数名称

参数名称是写入的lowerCamelCase请注意,即便参数须要构造函数,这也适用。

不该在公共方法中使用单字符参数名称。

例外:当第三方框架须要时,参数名称能够以a开头$此异常不适用于任何其余标识符(例如,局部变量或属性)。

6.2.8局部变量名称

lowerCamelCase如上所述,除了模块本地(顶级)常量外,写入局部变量名函数做用域中的常量仍然以lowerCamelCase请注意,即便变量包含构造函数,lowerCamelCase也适用。

6.2.9模板参数名称

模板参数名称应该是简洁的单字或单字母标识符,而且必须是所有大写字母,例如TYPETHIS

6.3 Camel案例:已定义

有时将英语短语转换为驼峰大小写的方法不止一种,例如当存在首字母缩略词或相似IPv6或 iOS的异常构造时为了提升可预测性,Google Style指定了如下(几乎)肯定性方案。

从名称的散文形式开始:

  1. 将短语转换为纯ASCII并删除任何撇号。例如, Müller的算法可能会成为Muellers算法
  2. 将此结果划分为单词,拆分空格和任何剩余的标点符号(一般为连字符)。
    1. 推荐:若是任何单词已经具备常规使用的传统驼峰外观,请将其拆分为其组成部分(例如, AdWords成为广告词)。请注意,像iOS这样的单词自己并非真正的驼峰; 它违反任何惯例,所以该建议不适用。
  3. 如今小写全部内容(包括首字母缩略词),而后只大写第一个字符:
    1. ......每一个字,以产生上骆驼的状况,或
    2. ...除了第一个以外的每一个词,以产生更低的骆驼案例
  4. 最后,将全部单词链接成一个标识符。

请注意,原始单词的大小几乎彻底被忽略。

例子:

散文形式 正确 不正确
XML HTTP请求 的XmlHttpRequest XMLHTTPRequest的
新客户ID newCustomerId newCustomerID
内秒表 innerStopwatch innerStopWatch
在iOS上支持IPv6? supportsIpv6OnIos supportsIPv6OnIOS
YouTube导入器 YouTubeImporter YoutubeImporter *

*可接受,但不推荐。

注意:有些单词在英语中含糊不清:例如,非空非空都是正确的,所以方法名称checkNonempty和checkNonEmpty一样都是正确的。

7 JSDoc

JSDoc用于全部类,字段和方法。

7.1通常表格

JSDoc块的基本格式以下例所示:

/ ** *这里写了多行JSDoc文本, *正常包裹。 * @param {number} arg要作某事的数字。 * / function doSomething(arg){...}

或者在这个单行示例中:

/ ** @const @private {!Foo}一小段JSDoc。* / this.foo_ = foo;

若是单行注释溢出到多行,则必须使用多行样式/***/它们本身的行。

许多工具从JSDoc注释中提取元数据以执行代码验证和优化。所以,这些评论必须是格式良好的。

7.2降价

JSDoc是用Markdown编写的,尽管它可能在必要时包含HTML。

请注意,自动提取JSDoc的工具(例如JsDossier)一般会忽略纯文本格式,所以若是您这样作:

/ ** *根据三个因素计算重量: *发送的项目 *收到的物品 *最后一个时间戳 * /

它会像这样出现:

根据三个因素计算权重:项目发送的项目是上次收到的时间戳 

相反,写一个Markdown列表:

/ ** *根据三个因素计算重量: * - 发送的项目 * - 收到的物品 * - 上次时间戳 * /

7.3 JSDoc标签

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注释JavaScriptClosure Type System中的类型

7.4换行

换行块标记缩进四个空格。包装的描述文本能够与前面的行描述对齐,但不鼓励这种水平对齐。

/ ** *说明用于长参数/返回描述的换行。 * @param {string} foo这是一个描述太长而没法适应的参数 * 一条线。 * @return {number}这会返回描述时间过长的内容 *适合一行。 * / exports.method = function(foo){ 返回5; };

包装@fileoverview描述时不要缩进

7.5顶级/文件级注释

文件可能具备顶级文件概述。版权声明,做者信息和默认可见性级别是可选的。当文件由多个类定义组成时,一般建议使用文件概述。顶级评论旨在将不熟悉代码的读者定位到此文件中。若是存在,它能够提供文件内容的描述以及任何依赖性或兼容性信息。包裹的线条不缩进。

例:

/ ** * @fileoverview文件描述,用途和信息 *关于它的依赖关系。 * @package * /

7.6课堂评论

必须使用描述和任何模板参数,实现的接口,可见性或其余适当的标记来记录类,接口和记录。类描述应该为读者提供足够的信息,以了解如何以及什么时候使用该类,以及正确使用该类所需的任何其余注意事项。构造函数可能省略了文本描述。@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}要返回的行中的下一个项目。* / 下一个() {} }

7.7枚举和typedef注释

必须记录枚举和typedef。公共枚举和typedef必须具备非空描述。可使用前一行的JSDoc注释记录单个枚举项。

/ ** *一种有用的类型联合,常常被重用。 * @typedef {!Bandersnatch |!BandersnatchType} * / 让CoolUnionType;

 / ** * bandersnatches的类型。 * @enum {string} * / const BandersnatchType = { / **这种真是太酷了。* / FRUMIOUS:'太酷了', / **不那么笨拙的那种。* / MANXOME:'manxome', };

Typedef对于定义短记录类型或联合,复杂函数或泛型类型的别名很是有用。对于包含许多字段的记录类型,应避免使用Typedef,由于它们不容许记录单个字段,也不容许使用模板或递归引用。对于大型唱片类型,请选择@record

7.8方法和功能注释

必须记录参数和返回类型。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]); });

7.9财产评论

必须记录属性类型。若是名称和类型提供了足够的文档来理解代码,则能够省略私有属性的描述。

公共导出的常量与属性的注释方式相同。对于@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;

7.10输入注释

类型的注释中发现@param@return@this,和@type标签,以及可选的@const@export和任何知名度的标签。附加到JSDoc标记的类型注释必须始终用大括号括起来。

7.10.1可空性

类型系统分别定义修饰符!?非null和可空。原始类型(undefinedstringnumberboolean, symbol,和function(...): ...),并记录文字({foo: string, bar: number})默认状况下非空。不要!为这些类型添加显式对象类型(ArrayElementMyClass,等等)是经过缺省为空的,但不能从做为名称被当即分辨@typedef“d到一个非空逐默认类型。所以,除了基元和记录文字以外的全部类型都必须使用?明确注释!指示它们是否可为空。

7.10.2类型转换

在类型检查不能准确推断表达式类型的状况下,能够经过添加类型注释注释并将表达式括在括号中来收紧类型。请注意,括号是必需的。

/ ** @type {number} * /(x) 

7.10.3模板参数类型

始终指定模板参数。这样编译器能够作得更好,它使读者更容易理解代码的做用。

坏:

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 用于类型层次结构而不是相似地图的结构。

7.11可见性注释

可见性注释(@private,, )能够在块中指定@package@protected也能够@fileoverview在任何导出的符号或属性上指定。不要指定局部变量的可见性,不管是在函数内仍是在模块的顶层。全部@private名称必须如下划线结尾。

8政策

8.1 Google风格未指定的问题:保持一致!

对于任何未经过此规范明确解决的样式问题,更喜欢执行同一文件中的其余代码已经在执行的操做。若是这不能解决问题,请考虑模拟同一包中的其余文件。

8.2编译器警告

8.2.1使用标准警告集

尽量使用项目--warning_level=VERBOSE

8.2.2如何处理警告

在作任何事情以前,请确保您彻底理解警告告诉您的内容。若是您不愿定为何会出现警告,请寻求帮助。

理解警告后,请按顺序尝试如下解决方案:

  1. 首先,修复它或解决它。强烈尝试实际处理警告,或找到另外一种方法来完成彻底避免这种状况的任务。
  2. 不然,肯定它是不是误报警。若是您确信警告无效且代码其实是安全和正确的,请添加注释以使读者相信这一事实并应用@suppress 注释。
  3. 不然,请留下TODO评论。这是最后的选择若是这样作,请不要取消警告。警告应该是可见的,直到能够正确处理。

8.2.3在最合理的范围内抑制警告

警告在最合理的范围内被抑制,一般是单个局部变量或很是小的方法。一般,仅为此缘由提取变量或方法。

/ ** @suppress {uselessCode}没法识别'use asm'声明* / function fn(){ '使用asm'; 返回0; }

即便是一个类中的大量抑制仍然比将整个类隐藏到这种类型的警告更好。

8.3弃用

标记已弃用的方法,类或带@deprecated注释的接口弃用评论必须包含简单明了的方向,以便人们修复其呼叫站点。

8.4不适用于Google风格的代码

您偶尔会遇到代码库中不符合Google风格的文件。这些可能来自收购,或者多是在Google Style在某个问题上占据一席以前编写的,或者可能因为任何其余缘由而处于非Google风格。

8.4.1从新格式化现有代码

更新现有代码的样式时,请遵循如下准则。

  1. 不须要更改全部现有代码以符合当前样式指南。从新格式化现有代码是代码流失和一致性之间的权衡。样式规则随着时间的推移而发展,这些调整以保持合规性会产生没必要要的流失。可是,若是对文件进行了重大更改,则预计该文件将采用Google风格。
  2. 注意不要让机会主义风格修复混淆CL的焦点。若是您发现本身进行了不少样式更改,这些更改对于CL的核心焦点并不重要,请将这些更改提高为单独的CL。

8.4.2新增代码:使用Google Style

全新文件使用Google Style,不管同一包中其余文件的样式选择如何。

将新代码添加到非Google风格的文件时,建议首先从新格式化现有代码,但需遵循8.4.1从新格式化现有代码中的建议 

若是未执行此从新格式化,则新代码应尽量与同一文件中的现有代码保持一致,但不得违反样式指南。

8.5本地风格规则

团队和项目可能会采用除本文档以外的其余样式规则,但必须接受清理更改可能不遵照这些附加规则,而且不得因违反任​​何其余规则而阻止此类清理更改。谨防过多的规则,没有任何目的。样式指南并不寻求在每种可能的场景中定义样式,也不该该。

8.6生成的代码:大多数是免除的

构建过程生成的源代码不须要采用Google Style。可是,任何从手写源代码引用的生成标识符都必须遵循命名要求。做为特殊例外,容许此类标识符包含下划线,这可能有助于避免与手写标识符冲突。

9附录

9.1 JSDoc标记引用

JSDoc在JavaScript中有多种用途。除了用于生成文档以外,它还用于控制工具。最着名的是Closure Compiler类型注释。

9.1.1键入注释和其余Closure Compiler注释

Closure编译器使用的JSDoc文档在闭包编译器的注释JavaScript闭包类型系统中的类型中进行了描述 

9.1.2文档注释

除了为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(){ // ... }

指示给定测试函数回归测试的错误。

多个错误应该各自都有本身的@bug行,以便尽量简单地搜索回归测试。

@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风格的一部分。

9.1.3特定于框架的注释

如下注释特定于特定框架。

骨架 标签 文档
角度1 @ngInject
聚合物 @polymerBehavior https://github.com/google/closure-compiler/wiki/Polymer-Pass

 

9.1.4关于标准Closure Compiler注释的注释

如下标记过去是标准的,但如今已弃用。

标签 模板和示例 描述
@expose @expose 已过期。不使用。使用@export和/或@nocollapse 代替。
@inheritDoc @inheritDoc 已过期。不使用。@override改用。

 

9.2常被误解的风格规则

如下是关于Google Style for JavaScript的一些不为人知或常被误解的事实的集合。(如下是真实陈述;这不是神话列表

  • @author源文件中既不须要版权声明也不须要信用。(二者都没有明确推荐。)
  • 除了第一个构造函数(5.4.1构造函数)以外,没有强硬的规则来管理如何对类的成员进行排序(5.4类)。
  • 空块一般能够简洁地表示为{},如(4.1.3空块:可能简明)中所述。
  • 换行的主要指令是:更倾向于在更高的句法层面打破4.5.1在哪里打破)。
  • 字符串文字,注释和Javadoc中容许使用非ASCII字符,实际上,当它们使代码比同等的Unicode转义2.3.3非ASCII字符更容易阅读时,建议使用非ASCII字符

存在如下工具以支持Google Style的各个方面。

9.3.1闭包编译器

该程序执行类型检查和其余检查,优化和其余转换(例如ECMAScript 6到ECMAScript 5代码下降)。

9.3.2 clang-format

该程序将JavaScript源代码从新格式化为Google Style,而且还遵循一些非必需但常常可读性加强的格式化实践。

clang-format不须要。容许做者改变其输出,容许审稿人要求进行此类更改; 争议以一般的方式解决。可是,子树能够选择在本地选择加入。

9.3.3 Closure编译器linter

该程序检查各类失误和反模式。

9.3.4一致性框架

JS一致性框架是一个工具,它是Closure Compiler的一部分,它为开发人员提供了一种简单的方法来指定一组额外的检查,以便在标准检查之上对其代码库运行。例如,一致性检查能够禁止访问某个属性,或者调用某个函数,或者丢失类型信息(未知数)。

这些规则一般用于强制执行关键限制(例如定义可能破坏代码库的全局变量)和安全模式(例如使用 eval或分配innerHTML),或者更松散地提升代码质量。

有关其余信息,请参阅JS一致性框架的官方文档 

9.4传统平台的例外状况

9.4.1概述

本节描述了当代码做者没法使用现代ECMAScript 6语法时要遵循的异常和其余规则。若是没法使用ECMAScript 6语法,则须要使用推荐样式的例外状况,并在此处概述:

  • var容许使用声明
  • 使用arguments被容许
  • 容许使用没有默认值的可选参数

9.4.2使用 var

9.4.2.1 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`是函数范围的,不是循环的本地。

9.4.2.2声明变量尽量接近首次使用

尽管var声明的范围限定在封闭函数的开头,但var为了便于阅读声明应尽量接近它们的首次使用。可是,var若是在块外部引用该变量,则不要在块中放置声明。例如:

function sillyFunction(){ var count = 0; for(var x in y){ //“count”能够在这里声明,但不要这样作。 计数++; } console.log(在y'中计算+'项目); }
9.4.2.3使用@const做为常量变量

对于const将使用关键字的全局声明,若是可用,则var使用@const 注释声明(对于局部变量,这是可选的)。

9.4.3不要使用块做用域函数声明

难道不是这样作:

if(x){ function foo(){} }

虽然在ECMAScript 6支持功能声明以前实现的大多数JavaScript VM都没有标准化。实现彼此不一致,而且与块范围函数声明的如今标准ECMAScript 6行为不一致。ECMAScript 5和previous仅容许在脚本或函数的根语句列表中进行函数声明,并在严格模式下在块做用域中明确禁止它们。

要得到一致的行为,请使用var带有函数表达式初始化来定义块中的函数:

if(x){ var foo = function(){}; }

9.4.4使用goog.provide/进行依赖管理goog.require

goog.provide已弃用。goog.module即便在具备现有goog.provide用途的项目中,也应使用全部新文件如下规则仅适用于预先存在的goog.provide文件。

9.4.4.1摘要
  • 将全部goog.provides放在第一位,goog.require第二位。单独提供须要空行。
  • 按字母顺序对条目排序(首先是大写)。
  • 不要包装goog.providegoog.require陈述。若有必要,可超过80列。
  • 仅提供顶级符号。

截至2016年10月,goog.providegoog.requiredependency管理已弃用全部新文件,即便在使用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');
9.4.4.2别名用 goog.scope

goog.scope已弃用。goog.scope即便在具备现有goog.scope使用的项目中,也不该使用新文件

goog.scope可用于缩短使用goog.providegoog.requiredependency管理的代码中对命名空间符号的引用

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
相关文章
相关标签/搜索