[Javascript] 编程实践之1: Google的Javascript代码风格4:格式化

4 格式化

术语注释:类块构造是指类,函数,方法或用大括号分隔的代码块的主体。 请注意,通过5.2 数组文法5.3 对象文法,可以将任何数组或对象文法视作一个类似于块的构造。

提示:使用clang格式。 JavaScript社区投入了很多精力来确保clang格式对JavaScript文件执行正确的操作。 clang格式已与几种流行的编辑器集成。

4.1 大括弧

4.1.1 在所有的控制结构中都使用大括弧

即使主体仅包含单个语句,所有控制结构(即 if, else, for, do, while,以及其他任何操作)都必须使用大括号。 非空块的第一条语句必须以其自己的行开始。

不允许:

if (someVeryLongCondition())
  doSomething();

for (let i = 0; i < foo.length; i++) bar(foo[i]);

例外:一个简单的if语句可以完全放在一行中而没有换行(并且没有其它),当它提高可读性时,可以放在一行中而没有大括号。 这是控件结构可能省略花括号和换行符的唯一情况。

if (shortCondition()) foo();
4.1.2 非空块的K&R风格

对于非空块或者类似块,大括弧遵循Kernighan和Ritchie style (Egyptian brackets) :

  • 左括弧之前没有换行。
  • 左括弧之后换行。
  • 右括弧前有换行。
  • 如果右括号终止一个语句或函数或类语句的主体或类方法,则在右括号后换行。 具体来说,如果在括号之后紧跟着else,catch,while或逗号,分号或右括号,则没有换行符。

例如:

class InnerClass {
  constructor() {}

  /** @param {number} foo */
  method(foo) {
    if (condition(foo)) {
      try {
        // Note: this might fail.
        something();
      } catch (err) {
        recover();
      }
    }
  }
}
4.1.3 空块:可能更精炼

空块或者类似块的构造:在打开后立即关闭,中间不能有字符,空格或者换行符(即{}),除非它是多块语句的一部分(例如直接包含多个区块:if/else或者try/catch/finally)。

例如:

function doNothing() {}

禁止:

if (condition) {
  // …
} else if (otherCondition) {} else {
  // …
}

try {
  // …
} catch (e) {}

4.2 块缩进:+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,
], bar);

允许其它组合,尤其是在强调元素之间的语义分组时,但不应仅用于减少较大数组的垂直大小。

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,
}, bar);
4.2.3 类文法

类文字(无论是声明还是表达式)都缩进为块。 不要在方法之后或类声明的右括号后添加分号(包含类表达式的语句(如赋值)仍以分号终止)。 除非该类扩展了模板化类型,否则请使用extend关键字,而不要使用@extends JSDoc注释。

示例:

class Foo {
  constructor() {
    /** @type {number} */
    this.x = 42;
  }

  /** @return {number} */
  method() {
    return this.x;
  }
}
Foo.Empty = class {};
/** @extends {Foo<string>} */
foo.Bar = class extends Foo {
  /** @override */
  method() {
    return super.method() / 2;
  }
};

/** @interface */
class Frobnicator {
  /** @param {string} message */
  frobnicate(message) {}
}
4.2.4 函数表达式

在函数调用的参数列表中声明匿名函数时,函数的主体缩进比前面的缩进深度多2个空格。

示例:

prefix.something.reallyLongFunctionName('whatever', (a1, a2) => {
  // Indent the function body +2 relative to indentation depth
  // of the 'prefix' statement one line above.
  if (a1.equals(a2)) {
    someOtherLongFunctionName(a1);
  } else {
    andNowForSomethingCompletelyDifferent(a2.parrot);
  }
});

some.reallyLongFunctionCall(arg1, arg2, arg3)
    .thatsWrapped()
    .then((result) => {
      // Indent the function body +2 relative to the indentation depth
      // of the '.then()' call.
      if (result) {
        result.use();
      }
    });
4.2.5 Switch语句

与其他任何块一样,切换块的内容缩进+2。

在开关标签之后,出现换行符,并且压痕级别增加了+2,就像在打开一个块一样。 如果词汇作用域需要,可以使用显式块。 以下开关标签将返回到先前的缩进级别,就好像一个块已被关闭一样。

在中断和以下情况之间,空行是可选的。例:

switch (animal) {
  case Animal.BANDERSNATCH:
    handleBandersnatch();
    break;

  case Animal.JABBERWOCK:
    handleJabberwock();
    break;

  default:
    throw new Error('Unknown animal');
}

4.3 语句

4.3.1 每行一条语句

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

4.3.2 分号是必须的

每个语句必须以分号结尾。禁止依靠自动分号插入。

4.4 列数限制:80

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

例外

  1. goog.module,goog.require和goog.requireType语句(请参见3.3 goog.module语句3.6 goog.require和goog.requireType语句)。
  2. ES模块从语句导入和导出(请参加3.4.1 导入 3.4.2.4 export from语句)。
  3. 不能遵守列限制或者妨碍列数限制的例子包括:
    • 需要在源中点击的一个长URL;
    • 用户复制和粘贴的shell命令;
    • 可能需要完全复制或者搜索的长字符串文字(例如长文件路径)。

4.5 换行

术语注释:换行将代码块分成多行以遵守列数限制,否则该代码块可以合法地容纳在一行中。

没有全面的确定性公式可以准确显示在每种情况下的换行方式。 通常,有几种有效的方法可以对同一段代码进行换行。

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

4.5.1 在哪里做行中断

换行的主要指令是:更倾向于在更高的语法级别上做行中断。

倾向于:

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

而不是:

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

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

运算符的包装如下:

  1. 当一行在一个操作符处结束时,在该操作符之后中断。 (请注意,这与Java的Google风格不同)
    - 这不适用于实际上不是运算符的点(.)。
  2. 方法或构造函数名称保持附加在其后的括号(()中。
  3. 逗号(,)始终附加在其前面的令牌上。

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

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

换行时,第一行(每条连续行)之后的每一行都应从原始行开始缩进至少+4,除非它属于块缩进规则。

当有多个连续行时,缩进可以适当地更改为超过+4。 通常,在更深的句法级别上的连续行以4的较大倍数缩进,并且当且仅当它们以句法上平行的元素开始时,两条行才使用相同的缩进级别。

4.6.3 水平对齐:不鼓励:不鼓励使用不固定数量的空格将某些标记与前几行对齐。

4.6 空格

4.6.1 垂直空格

出现一个空白行:

  1. 在类或对象文字中的连续方法之间
    • 例外:对象文字中两个连续属性定义之间的空白行(它们之间没有其他代码)是可选的。 根据需要使用此类空行来创建字段的逻辑分组。
  2. 在方法主体内,谨慎地创建语句的逻辑分组。 在功能主体的开头或结尾处不允许有空行。
  3. 可选地,在类或对象文字中的第一个方法或最后一个方法之后(既不支持也不反对)。
  4. 根据本文档其他部分的要求(例如3.6 goog.require和goog.requireType语句)。

允许使用多个连续的空行,但从不要求(也不鼓励)。

4.6.2 水平空格

水平空白的使用取决于位置,分为三大类:前导(在行的开头),尾随(在行的结尾)和内部。 前导空格(即缩进)在其他地方介绍。 禁止尾随空格。

除了语言或其他样式规则的要求之外,除了文字,注释和JSDoc外,单个内部ASCII空间也仅出现在以下位置。

  1. 将函数和超级除外的所有保留字(例如if,for或catch)与该行后跟的开放括号(())分开。
  2. 将任何保留字(例如else或catch)与该行之前的右花括号(})分开。
  3. 在任何大括号({)之前,但有两个例外:
    • 在对象常量之前,该对象常量是函数的第一个参数或数组常量中的第一个元素(例如foo({a:[{c:d}]})。
    • 在模板扩展中,如语言所禁止(例如有效:ab $ {1 + 2} cd,无效:xy $ {3} z)。
  4. 在任何二元或三元运算符的两侧。
  5. 在逗号(,)或分号(;)之后。请注意,这些字符之前绝对不允许有空格。
  6. 对象文字中冒号(:)之后。
  7. 在双斜杠(//)的两边,开始行尾注释。在此,允许多个空格,但不是必需的。
  8. 在开放块注释字符之后和在封闭字符的两侧(例如,用于简短形式的类型声明,强制类型转换和参数名称注释):
    this.foo = / ** @type {number} * /(bar);或函数(/ *字符串 / foo){;或baz(/ * buzz = * / true))。
4.6.3 水平对齐:不鼓励

术语注:水平对齐是在代码中添加可变数量的附加空格的一种做法,目的是使某些标记直接出现在前几行中其他标记的下面。

允许这种做法,但Google风格通常不鼓励这样做。 甚至不需要在已经使用过的地方保持水平对齐。

这是一个不带对齐的示例,然后是一个带对齐的示例。 两者都允许,但不鼓励后者:

{
  tiny: 42, // this is great
  longer: 435, // this too
};

{
  tiny:   42,  // permitted, but future edits
  longer: 435, // may leave it unaligned
};

提示:对齐可以提高可读性,但会给以后的维护带来麻烦。 考虑未来的变化,只需要触碰一条线。 此更改可能会使原本令人愉悦的格式受到干扰,这是允许的。 通常,它会提示编码员(也许是您)也调整附近行的空白,可能触发一系列级联的重新格式化。 单行更改现在具有爆炸半径。 在最坏的情况下,这可能会导致毫无意义的繁忙工作,但在最佳情况下,它仍会破坏版本历史记录信息,减慢审阅者的速度并加剧合并冲突。

4.6.4 函数参数

最好将所有函数参数与函数名称放在同一行。 如果这样做会超过80列的限制,则必须以可读的方式将参数换行。 为了节省空间,您可以尽可能地将其换行为80,或者将每个参数放在自己的行上以提高可读性。 缩进应为四个空格。 允许与括号对齐,但不鼓励这样做。 以下是参数包装的最常见模式:

// Arguments start on a new line, indented four spaces. Preferred when the
// arguments don't fit on the same line with the function name (or the keyword
// "function") but fit entirely on the second line. Works with very long
// function names, survives renaming without reindenting, low on space.
doSomething(
    descriptiveArgumentOne, descriptiveArgumentTwo, descriptiveArgumentThree) {
  // …
}

// If the argument list is longer, wrap at 80. Uses less vertical space,
// but violates the rectangle rule and is thus not recommended.
doSomething(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,
    tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {
  // …
}

// Four-space, one argument per line.  Works with long function names,
// survives renaming, and emphasizes each argument.
doSomething(
    veryDescriptiveArgumentNumberOne,
    veryDescriptiveArgumentTwo,
    tableModelEventHandlerProxy,
    artichokeDescriptorAdapterIterator) {
  // …
}

4.7 括弧分组:推荐

可选的分组括弧只有在作者和审阅者一致认为,没有括弧时代码仍然不会被误读, 以及省略括弧也不会使得代码更易于阅读时,才会被省略。假设每个读者都记住了整个运算符优先级表是不合理的。

不要为如下这些词后面的表达式上添加不必要的括弧:delete,typeof,void,return,throw,case,in,of以及yield。

对于类型转换,括弧是必须的,例如:/** @type {!Foo} */ (foo)。

4.8 注释

这部分描述实现注释。JSDoc则另外在7 JSDoc中进行详述。

4.8.1 块注释风格

块注释被缩进到与周围代码相同的级别。它们可能是//或//风格的。对于多行//注释,后面的行必须以与前一行的对齐的开始,这样注释就很明显,没有额外的上下文。可选的分组括号只有在作者和审阅者一致认为没有括号,代码就不会被误读的情况,以及有了代码也不会使得代码更易于阅读的情况下才被忽略。假设每个阅读者都记住了整个运算符优先表是不合理的。

/*
 * This is
 * okay.
 */

// And so
// is this.

/* This is fine, too. */

注释不包括在带有星号或其它字符的框中。

不要使用JSDoc(/**…*/)作为实现注释。

4.8.2 参数名注释

当值和方法名不能充分表达其含义,并且重构方法也不可以使得其更清晰时,应该使用“参数名”注释。它们的首选格式是在值之前添加=。

someFunction(obviousParam, /* shouldRender= */ true, /* name= */ 'hello');

为了与周围的代码保持一致,你可以把它放在后面没有=的后面。

someFunction(obviousParam, true /* shouldRender */, 'hello' /* name */);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值