9 附录
9.1 JSDoc 标签参考
JSDoc 在 JavaScript 中有多种用途。除了用于生成文档外,它还用于控制工具。最知名的是 Closure Compiler 类型注解。
9.1.1 类型注解和其他 Closure Compiler 注解
Closure Compiler 使用的 JSDoc 文档描述在 Annotating JavaScript for the Closure Compiler 和 Types in the Closure Type System 中。
9.1.2 文档注解
除了 Annotating JavaScript for the Closure Compiler 中描述的 JSDoc 外,以下标签是常见的,并且被各种文档生成工具(如 JsDossier )良好支持,纯粹用于文档目的。
9.1.2.1 @author 或 @owner - 不推荐。
不推荐。
语法:@author username@google.com (First Last)
/**
* @fileoverview Utilities for handling textareas.
* @author kuth@google.com (Uthur Pendragon)
*/记录文件的作者或测试的所有者,通常仅在 @fileoverview
注释中使用。@owner 标签由单元测试仪表板使用,用于确定谁拥有测试结果。
9.1.2.2 @bug
语法:@bug bugnumber
/** @bug 1234567 */
function testSomething() {
// …
}
/**
* @bug 1234568
* @bug 1234569
*/
function testTwoBugs() {
// …
}指示给定的测试函数回归测试了哪些 bug。
多个 bug 应各自有自己的 @bug 行,以使搜索回归测试尽可能容易。
9.1.2.3 @code - 已弃用。不要使用。
已弃用。不要使用。请使用 Markdown 反引号代替。
语法:{@code ...}
历史上,`BatchItem` 被写作 {@code BatchItem}。
/** Processes pending `BatchItem` instances. */
function processBatchItems() {}指示 JSDoc 描述中的术语是代码,以便在生成的文档中正确格式化。
9.1.2.4 @desc
语法:@desc Message description
/** @desc Notifying a user that their account has been created. */
exports.MSG_ACCOUNT_CREATED = goog.getMsg(
'Your account has been successfully created.');9.1.2.5 @link
语法:{@link ...}
此标签用于在生成的文档中生成交叉引用链接。
/** Processes pending {@link BatchItem} instances. */
function processBatchItems() {}历史说明:@link 标签也曾用于在生成的文档中创建外部链接。对于外部链接,请始终使用 Markdown 的链接语法代替:
/**
* This class implements a useful subset of the
* [native Event interface](https://dom.spec.whatwg.org/#event).
*/
class ApplicationEvent {}9.1.2.6 @see
语法:@see Link
/**
* Adds a single item, recklessly.
* @see #addSafely
* @see goog.Collect
* @see goog.RecklessAdder#add
*/引用查找另一个类的函数或方法。
9.1.2.7 @supported
语法:@supported Description
/**
* @fileoverview Event Manager
* Provides an abstracted interface to the browsers' event systems.
* @supported IE10+, Chrome, Safari
*/在 fileoverview 中用于指示文件支持哪些浏览器。
你也可能在第三方代码中看到其他类型的 JSDoc 注解。这些注解出现在 JSDoc Toolkit Tag Reference 中,但不被视为有效的 Google 风格的一部分。
9.1.3 框架特定的注解
以下注解特定于某个特定框架。
9.1.3.1 @ngInject 用于 Angular 1
9.1.3.2 @polymerBehavior 用于 Polymer
https://github.com/google/closure-compiler/wiki/Polymer-Pass
9.1.4 关于标准 Closure Compiler 注解的说明
以下标签曾经是标准的,但现在已弃用。
9.1.4.1 @expose - 已弃用。不要使用。
已弃用。不要使用。请使用 @export 和/或 @nocollapse 代替。
9.1.4.2 @inheritDoc - 已弃用。不要使用。
已弃用。不要使用。请使用 @override 代替。
9.2 常被误解的风格规则
以下是关于 Google JavaScript 风格中鲜为人知或常被误解的事实的集合。(以下是正确的陈述;这不是“谣言”列表。)
- 源文件中不需要版权声明或
@author署名。(也没有明确推荐。) - 关于如何排列类的成员,没有“硬性”规则(??)。
- 空块通常可以简洁地表示为
{},详见(??)。 - 换行的首要准则是:优先在更高的语法级别处断行(??)。
- 字符串字面量、注释和 JSDoc 中允许使用非 ASCII 字符,事实上,当它们比等效的 Unicode 转义使代码更易于阅读时,推荐使用(??)。
9.3 风格相关工具
以下工具用于支持 Google 风格的各个方面。
9.3.1 Closure Compiler
此程序执行类型检查和其他检查、优化及其他转换(例如将代码降级到 ECMAScript 5)。
9.3.2 clang-format
此程序将 JavaScript 源代码重新格式化为 Google
风格,并且还遵循许多不是必需的但经常增强可读性的格式化实践。clang-format
产生的输出符合风格指南。
clang-format
不是必需的。作者可以更改其输出,审阅者也可以要求进行此类更改;争议以通常的方式解决。但是,子树可以选择在本地强制执行。
9.3.3 Closure compiler linter
此程序检查各种失误和反模式。
9.3.4 一致性框架(Conformance framework)
JS 一致性框架是 Closure Compiler
的一部分工具,为开发者提供了一种简单的方式来指定一组针对其代码库运行的额外检查,这些检查超出标准检查。一致性检查可以例如禁止访问某个属性,或调用某个函数,或缺少类型信息(unknown)。
这些规则通常用于强制执行关键限制(例如定义全局变量,这可能破坏代码库)和安全模式(例如使用
eval 或赋值给 innerHTML),或更宽松地用于提高代码质量。
更多信息请参见 JS Conformance Framework 的官方文档。
9.4 旧平台的例外
9.4.1 概述
本节描述了当代码作者无法使用现代 ECMAScript 语法时需要遵循的例外和额外规则。当无法使用现代 ECMAScript 语法时,需要对推荐风格进行例外处理,概述如下:
- 允许使用
var声明 - 允许使用
arguments - 允许使用没有默认值的可选参数
9.4.2 使用 var
9.4.2.1 var 声明不是块作用域的
var
声明的作用域是最近的封闭函数、脚本或模块的开头,这可能导致意外行为,特别是在循环内引用
var 声明的函数闭包(closure)中。以下代码给出了一个示例:
for (var i = 0; i < 3; ++i) {
var iteration = i;
setTimeout(function() { console.log(iteration); }, i*1000);
}
// logs 2, 2, 2 -- NOT 0, 1, 2
// because `iteration` is function-scoped, not local to the loop.9.4.2.2 尽可能在首次使用的位置附近声明变量
尽管 var 声明的作用域是封闭函数的开头,但为了可读性,var
声明应尽可能靠近其首次使用的位置。但是,如果变量在块外被引用,不要将
var 声明放在块内。例如:
function sillyFunction() {
var count = 0;
for (var x in y) {
// "count" could be declared here, but don't do that.
count++;
}
console.log(count + ' items in y');
}9.4.2.3 对常量变量使用 @const
对于如果 const 关键字可用就会使用 const 的全局声明,用 @const 注解
var 声明代替(对局部变量这是可选的)。
9.4.3 不要使用块作用域的函数声明
不要这样做:
if (x) {
function foo() {}
}虽然在 ECMAScript 6 之前实现的大多数 JavaScript 虚拟机支持块内的函数声明,但它并未被标准化。各实现之间以及与现在标准的块作用域函数声明的 ECMAScript 行为之间不一致。ECMAScript 5 标准及之前版本仅允许在脚本或函数的根语句列表中进行函数声明,并且在严格模式下明确禁止在块作用域中进行函数声明。
为了获得一致的行为,请使用用函数表达式初始化的 var 来在块内定义函数:
if (x) {
var foo = function() {};
}9.4.4 使用 goog.provide/goog.require 进行依赖管理
9.4.4.1 摘要
**警告:goog.provide 依赖管理已弃用。**所有新文件,即使在使用
goog.provide 的旧文件的项目中,也应使用
goog.module。以下规则仅适用于已有的
goog.provide 文件。
- 将所有
goog.provide放在前面,goog.require放在后面。用空行分隔goog.provide和goog.require。 - 按字母顺序排列条目(大写优先)。
- 不要换行
goog.provide和goog.require语句。如有必要,超过 80 列。 - 只
provide顶级符号。
goog.provide 语句应分组在一起并放在最前面。所有 goog.require
语句应随后。两个列表之间应用空行分隔。
与其他语言中的 import 语句类似,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');定义在同一个类上的所有成员应该在同一个文件中。只有顶级类应在包含多个定义在同一类上的成员(例如枚举、内部类等)的文件中进行 provide。
这样做:
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');命名空间上的成员也可以 provide:
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.provide/goog.require
依赖管理的代码中缩短对命名空间符号的引用。
每个文件只能添加一个 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;
// Alias new types after the constructor declaration.
my.module.SomeType = function() { ... };
var SomeType = my.module.SomeType;
// Declare methods on the prototype as usual:
SomeType.prototype.findButton = function() {
// Button as aliased above.
this.button = new Button(dom.getElement('my-button'));
};
...
}); // goog.scope9.4.4.3 goog.forwardDeclare
优先使用 goog.requireType 而不是 goog.forwardDeclare
来打破同一库中文件之间的循环依赖。与 goog.require
不同,goog.requireType 语句允许在命名空间定义之前导入它。
goog.forwardDeclare 语句必须遵循与 goog.require 和
goog.requireType 相同的风格规则。goog.forwardDeclare、goog.require
和 goog.requireType 的整个块按字母顺序排列。
goog.forwardDeclare
在旧代码中用于打破跨库边界的循环引用。然而,此模式受到构建工具的不良支持,不应使用。代码应组织以避免跨库的循环依赖(通过拆分/合并库)。
9.4.4.4 goog.module.get(name)
如果 goog.provide 文件依赖于 goog.module 文件,goog.provide
文件通常无法通过全局名称引用模块的导出。相反,除了 goog.require()
模块外,goog.provide 文件必须通过调用 goog.module.get('module.name')
来获取模块的导出对象。
注意:仅调用 goog.module.get('module.name')
不会创建你的代码对模块的构建时依赖。需要 goog.require 来建立构建依赖。
9.4.4.5 goog.module.declareLegacyNamespace()
警告:goog.module.declareLegacyNamespace 仅用于过渡期使用。
goog.module.declareLegacyNamespace 仅用于将 JavaScript
文件及其消费者从 goog.provide 迁移到 goog.module 时使用。将你的
goog.module 的消费者更新为自身也使用
goog.module。尽可能删除对 goog.module.declareLegacyNamespace
的调用。
如果你无法尽快将旧命名空间的消费者从 goog.provide 更新到
goog.module,请将文件内容包裹在 goog.scope 调用中,使用
goog.module.get 导入旧命名空间——然后删除你的 goog.module 中对
goog.module.declareLegacyNamespace 的调用。
在 goog.module(name) 内调用 goog.module.declareLegacyNamespace()
将像 goog.provide() 调用一样将模块的命名空间声明为全局名称。这允许非
goog.module 命名空间在不调用 goog.module.get(name)
的情况下访问模块的导出。