[Javascript] 编程实践之1: Google的Javascript代码风格7:JSDoc

7 JSDoc

JSDoc在所有类、域以及方法中被使用。

7.1 通用形式

在此示例中可以看到JSDoc块的基本格式:

/**
 * Multiple lines of JSDoc text are written here,
 * wrapped normally.
 * @param {number} arg A number to do something to.
 */
function doSomething(arg) {}

或者在此单行示例中:

/** @const @private {!Foo} A short bit of JSDoc. */
this.foo_ = foo;

如果单行注释溢出到多行,则必须在自己的行上使用带有 / ** 和 */ 的多行样式。

许多工具从JSDoc注释中提取元数据以执行代码验证和优化。 因此,这些注释必须格式正确。

7.2 Markdown

JSDoc用Markdown编写,尽管必要时它可能包括HTML。

请注意,自动提取JSDoc的工具(例如JsDossier)通常会忽略纯文本格式,因此如果您这样做:

/**
 * Computes weight based on three factors:
 *   items sent
 *   items received
 *   last timestamp
 */

那么它会像这样出来:

Computes weight based on three factors: items sent items received last timestamp

相反,以如下的方式写Markdown的列表:

/**
 * Computes weight based on three factors:
 *
 *  - items sent
 *  - items received
 *  - last timestamp
 */

7.3 JSDoc标签

Google样式允许使用JSDoc标签的子集。 有关完整列表,请参见9.1 JSDoc标签参考。 大多数标签必须占据自己的行,并且标签应位于行的开头。

不允许:

/**
 * The "param" tag must occupy its own line and may not be combined.
 * @param {number} left @param {number} right
 */
function add(left, right) { ... }

不需要任何其他数据的简单标签(例如@ private,@ const,@ final,@ export)可以与同一行以及适当的可选类型组合到同一行上。

/**
 * Place more complex annotations (like "implements" and "template")
 * on their own lines.  Multiple simple tags (like "export" and "final")
 * may be combined in one line.
 * @export @final
 * @implements {Iterable<TYPE>}
 * @template TYPE
 */
class MyClass {
  /**
   * @param {!ObjType} obj Some object.
   * @param {number=} num An optional number.
   */
  constructor(obj, num = 42) {
    /** @private @const {!Array<!ObjType|number>} */
    this.data_ = [obj, num];
  }
}

对于何时组合标签或顺序不存在硬性规定,但是要保持一致。

有关在JavaScript中注释类型的常规信息,请参见Annotating JavaScript for the Closure CompilerTypes in the Closure Type System

7.4 换行

行包装的块标签缩进了四个空格。 包装好的描述文字可能与前几行的描述对齐,但是不建议这种水平对齐方式。

/**
 * Illustrates line wrapping for long param/return descriptions.
 * @param {string} foo This is a param with a description too long to fit in
 *     one line.
 * @return {number} This returns something that has a description too long to
 *     fit in one line.
 */
exports.method = function(foo) {
  return 5;
};

包装@desc或@fileoverview描述时,请勿缩进。

7.5 顶级/文件级注释

文件可能具有顶级文件概述。 版权声明,作者信息和默认可见性级别是可选的。 通常,只要文件包含多个类定义,通常建议使用文件概述。 顶层注释旨在使不熟悉该代码的读者适应此文件中的内容。 如果存在,它可以提供文件内容以及任何依赖关系或兼容性信息的描述。 包装的线不缩进。

例:

/**
 * @fileoverview Description of file, its uses and information
 * about its dependencies.
 * @package
 */

7.6 类注释

类,接口和记录必须带有描述和任何模板参数,已实现的接口,可见性或其他适当的标签,以文档形式记录。 类说明应为读者提供足够的信息,以了解如何以及何时使用该类,以及正确使用该类所需的任何其他注意事项。 构造函数上可能会省略文字说明。 @constructor和@extends批注不能与class关键字一起使用,除非该类用于声明@interface或扩展了通用类。

/**
 * A fancier event target that does cool things.
 * @implements {Iterable<string>}
 */
class MyFancyTarget extends EventTarget {
  /**
   * @param {string} arg1 An argument that makes this more interesting.
   * @param {!Array<number>} arg2 List of numbers to be processed.
   */
  constructor(arg1, arg2) {
    // ...
  }
};

/**
 * Records are also helpful.
 * @extends {Iterator<TYPE>}
 * @record
 * @template TYPE
 */
class Listable {
  /** @return {TYPE} The next item in line to be returned. */
  next() {}
}

7.7 Enum和typedef注释

所有枚举和typedef必须在上一行用适当的JSDoc标记(@typedef或@enum)进行记录。 公共枚举和typedef也必须有描述。 单个枚举项可能在上一行带有JSDoc注释的文档中。

/**
 * A useful type union, which is reused often.
 * @typedef {!Bandersnatch|!BandersnatchType}
 */
let CoolUnionType;


/**
 * Types of bandersnatches.
 * @enum {string}
 */
const BandersnatchType = {
  /** This kind is really frumious. */
  FRUMIOUS: 'frumious',
  /** The less-frumious kind. */
  MANXOME: 'manxome',
};

Typedef对于定义短记录类型或联合,复杂函数或通用类型的别名很有用。 对于具有许多字段的记录类型,应避免使用Typedef,因为它们不允许记录单个字段,也不允许使用模板或递归引用。 对于大记录类型,请使用@record。

7.8 方法和函数注释

在方法和命名函数中,必须记录参数和返回类型,除非相同签名@overrides省略所有类型。必要时应记录此类型。如果函数没有非空的return语句,则可以省略返回类型。

如果方法,参数和返回描述(但不是类型)在方法的其余JSDoc或其签名中显而易见,则可以省略。

方法描述以动词短语开头,该动词短语描述方法的作用。这个短语不是命令性的句子,而是用第三人称写的,好像在它之前有一个隐含的This方法。

如果方法覆盖超类方法,则它必须包含@override注释。覆盖的方法从超类方法继承所有JSDoc注释(包括可见性注释),并且应在覆盖的方法中将其省略。但是,如果在类型注释中完善了任何类型,则必须显式指定所有@param和@return注释。

/** A class that does something. */
class SomeClass extends SomeBaseClass {
  /**
   * Operates on an instance of MyClass and returns something.
   * @param {!MyClass} obj An object that for some reason needs detailed
   *     explanation that spans multiple lines.
   * @param {!OtherClass} obviousOtherClass
   * @return {boolean} Whether something occurred.
   */
  someMethod(obj, obviousOtherClass) { ... }

  /** @override */
  overriddenMethod(param) { ... }
}

/**
 * Demonstrates how top-level functions follow the same rules.  This one
 * makes an array.
 * @param {TYPE} arg
 * @return {!Array<TYPE>}
 * @template TYPE
 */
function makeArray(arg) { ... }

如果只需要记录函数的参数和返回类型,则可以选择在函数的签名中使用内联JSDocs。 这些内联JSDocs指定不带标签的return和param类型。

function /** string */ foo(/** number */ arg) {...}

如果需要描述或标签,请在方法上方使用单个JSDoc注释。 例如,返回值的方法需要@return标记。

class MyClass {
  /**
   * @param {number} arg
   * @return {string}
   */
  bar(arg) {...}
}
// Illegal inline JSDocs.

class MyClass {
  /** @return {string} */ foo() {...}
}

/** Function description. */ bar() {...}

在匿名函数中,注释通常是可选的。 如果自动类型推断不足或显式注释提高了可读性,则对param进行注释并返回如下类型:

promise.then(
    /** @return {string} */
    (/** !Array<string> */ items) => {
      doSomethingWith(items);
      return items[0];
    });

对于函数类型的表达式,请参见7.10.4 函数类型表达式

7.9 属性注释

属性类型必须记录在案。 如果名称和类型提供了足够的文档来理解代码,则可以省略私有属性的描述。

公开导出的常量与属性的注释方式相同。

/** My class. */
class MyClass {
  /** @param {string=} someString */
  constructor(someString = 'default string') {
    /** @private @const {string} */
    this.someString_ = someString;

    /** @private @const {!OtherType} */
    this.someOtherThing_ = functionThatReturnsAThing();

    /**
     * Maximum number of things per pane.
     * @type {number}
     */
    this.someProperty = 4;
  }
}

/**
 * The number of times we'll try before giving up.
 * @const {number}
 */
MyClass.RETRY_COUNT = 33;

7.10 Type注释

类型注释位于@ param,@ return,@ this和@type标记上,还可以在@ const,@ export和任何可见性标记上找到。 附加到JSDoc标记的类型注释必须始终用大括号括起来。

7.10.1 可空性

类型系统定义修饰符! 和? 分别用于非null和nullable。 这些修饰符必须在类型之前。

可空性修饰符对不同类型有不同的要求,分为两大类:

  1. 原语(字符串,数字,布尔值,符号,未定义,null)和文字({function(…):…}和{{foo:string …}})的类型注释始终默认不可为空。 使用 ? 修饰符使其可为空,但省略多余的!。
  2. 引用类型(通常,UpperCamelCase中的任何内容,包括some.namespace.ReferenceType)都引用在其他地方定义的类,枚举,记录或typedef。 由于这些类型可能为空,也可能不会为空,因此无法仅通过名称来判断它是否可为空。 始终使用显式? 和! 这些类型的修饰符,以防止在使用现场产生歧义。

不好的示例:

const /** MyObject */ myObject = null; // Non-primitive types must be annotated.
const /** !number */ someNum = 5; // Primitives are non-nullable by default.
const /** number? */ someNullableNum = null; // ? should precede the type.
const /** !{foo: string, bar: number} */ record = ...; // Already non-nullable.
const /** MyTypeDef */ def = ...; // Not sure if MyTypeDef is nullable.

// Not sure if object (nullable), enum (non-nullable, unless otherwise
// specified), or typedef (depends on definition).
const /** SomeCamelCaseName */ n = ...;

好的示例:

const /** ?MyObject */ myObject = null;
const /** number */ someNum = 5;
const /** ?number */ someNullableNum = null;
const /** {foo: string, bar: number} */ record = ...;
const /** !MyTypeDef */ def = ...;
const /** ?SomeCamelCaseName */ n = ...;
7.10.2 类型转换

如果编译器无法准确推断出表达式的类型,而goog.asserts中的断言函数无法对其进行补救,则可以通过添加类型注释并将表达式括在括号中来加紧类型。 请注意,括号是必需的。

/** @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 /** !Promise<!Response> */ response = ...;

const /** !Promise<undefined> */ thisPromiseReturnsNothingButParameterIsStillUseful = ...;
const /** !Object<string, *> */ mapOfEverything = {};

不应使用模板参数的情况:

  • 对象用于类型层次结构,而不是类似map的结构。
7.10.4 函数类型表达式

术语注:函数类型表达式是指函数类型的类型注释,注释中带有关键字function(请参见下面的示例)。

在给出函数定义的地方,请勿使用函数类型表达式。 使用@param和@return或内联注释指定参数和返回类型(请参见7.8 方法和函数注释)。 这包括匿名函数和已定义并分配给const的函数(其中jsdoc函数出现在整个赋值表达式上方)。

函数类型表达式是必需的,例如,在@ typedef,@ param或@return内部。 如果未立即使用函数定义对其进行初始化,则也可将其用于函数类型的变量或属性。

/** @private {function(string): string} */
this.idGenerator_ = googFunctions.identity;

使用函数类型表达式时,请始终明确指定返回类型。 否则,默认的返回类型是未知的(?),这会导致奇怪和意外的行为,并且很少是实际需要的。

坏习惯-类型错误,但未给出警告:

/** @param {function()} generateNumber */
function foo(generateNumber) {
  const /** number */ x = generateNumber();  // No compile-time type error here.
}

foo(() => 'clearly not a number');

好习惯:

/**
 * @param {function(): *} inputFunction1 Can return any type.
 * @param {function(): undefined} inputFunction2 Definitely doesn't return
 *      anything.
 * NOTE: the return type of `foo` itself is safely implied to be {undefined}.
 */
function foo(inputFunction1, inputFunction2) {...}
7.10.5 空格

在类型注释中,每个逗号或冒号后需要一个空格或换行符。 可以插入其他换行符以提高可读性或避免超出列限制。 这些中断应按照适用的准则进行选择和缩进(例如4.5 换行4.2 块缩进:+2个空格)。 类型注释中不允许使用其他空格。

好习惯:

/** @type {function(string): number} */

/** @type {{foo: number, bar: number}} */

/** @type {number|string} */

/** @type {!Object<string, string>} */

/** @type {function(this: Object<string, string>, number): string} */

/**
 * @type {function(
 *     !SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak,
 *     !OtherVeryLongTypedef): string}
 */

/**
 * @type {!SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak|
 *     !OtherVeryLongTypedef}
 */

坏习惯:

// Only put a space after the colon
/** @type {function(string) : number} */

// Put spaces after colons and commas
/** @type {{foo:number,bar:number}} */

// No space in union types
/** @type {number | string} */

7.11 可见性注释

可见性注释(@ private,@ package,@ protected)可以在@fileoverview块或任何导出的符号或属性中指定。 不要为函数或模块的顶层指定局部变量的可见性。 所有@private名称必须以下划线结尾。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值