rogerxu / rogerxu.github.io

Roger Xu's Blog
3 stars 2 forks source link

JavaScript Style Guide #30

Open rogerxu opened 8 years ago

rogerxu commented 8 years ago

airbnb/javascript: JavaScript Style Guide Google JavaScript Style Guide bendc/frontend-guidelines: Some HTML, CSS and JS best practices.

rogerxu commented 8 years ago

1. Types

rogerxu commented 8 years ago

2. References

rogerxu commented 8 years ago

3. Objects

rogerxu commented 8 years ago

4. Arrays

rogerxu commented 8 years ago

5. Destructuring

rogerxu commented 8 years ago

6. Strings

rogerxu commented 8 years ago

7. Functions

rogerxu commented 8 years ago

8. Arrow Functions

rogerxu commented 8 years ago

9. Classes & Constructors

rogerxu commented 8 years ago

10. Modules

rogerxu commented 8 years ago

11. Iterators and Generators

rogerxu commented 8 years ago

12. Properties

rogerxu commented 8 years ago

13. Variables

rogerxu commented 8 years ago

14. Hoisting

rogerxu commented 8 years ago

15. Comparison Operators & Equality

rogerxu commented 8 years ago

16. Blocks

rogerxu commented 8 years ago

17. Control Statements

rogerxu commented 8 years ago

18. Comments

rogerxu commented 8 years ago

19. Whitespace

rogerxu commented 8 years ago

20. Commas

rogerxu commented 8 years ago

21. Semicolons

rogerxu commented 8 years ago

22. Type Casting & Coercion

rogerxu commented 8 years ago

23. Naming Conventions

rogerxu commented 8 years ago

24. Accessors

rogerxu commented 8 years ago

25. Events

rogerxu commented 6 years ago

26. jQuery

rogerxu commented 6 years ago

27. ECMAScript 5 Compatibility

ECMAScript 5 compatibility table

rogerxu commented 6 years ago

28. ECMAScript 6+ (ES 2015+) Styles

rogerxu commented 6 years ago

29. Standard Library

rogerxu commented 6 years ago

30. Testing

rogerxu commented 6 years ago

Performance

rogerxu commented 6 years ago

HTML

Semantics

HTML5 provides us with lots of semantic elements aimed to describe precisely the content. Make sure you benefit from its rich vocabulary.

Make sure you understand the semantics of the elements you're using. It's worse to use a semantic element in a wrong way than staying neutral.

<!-- bad -->
<h1>
  <figure>
    <img alt=Company src=logo.png>
  </figure>
</h1>

<!-- good -->
<h1>
  <img alt=Company src=logo.png>
</h1>

Brevity

Keep your code terse. Forget about your old XHTML habits.

<!-- bad -->
<!doctype html>
<html lang=en>
  <head>
    <meta http-equiv=Content-Type content="text/html; charset=utf-8" />
    <title>Contact</title>
    <link rel=stylesheet href=style.css type=text/css />
  </head>
  <body>
    <h1>Contact me</h1>
    <label>
      Email address:
      <input type=email placeholder=you@email.com required=required />
    </label>
    <script src=main.js type=text/javascript></script>
  </body>
</html>

<!-- good -->
<!doctype html>
<html lang=en>
  <meta charset=utf-8>
  <title>Contact</title>
  <link rel=stylesheet href=style.css>

  <h1>Contact me</h1>
  <label>
    Email address:
    <input type=email placeholder=you@email.com required>
  </label>
  <script src=main.js></script>
</html>

Accessibility

Accessibility shouldn't be an afterthought. You don't have to be a WCAG expert to improve your website, you can start immediately by fixing the little things that make a huge difference, such as:

<!-- bad -->
<h1><img alt=Logo src=logo.png></h1>

<!-- good -->
<h1><img alt=Company src=logo.png></h1>

Language

While defining the language and character encoding is optional, it's recommended to always declare both at document level, even if they're specified in your HTTP headers. Favor UTF-8 over any other character encoding.

<!-- bad -->
<!doctype html>
<title>Hello, world.</title>

<!-- good -->
<!doctype html>
<html lang=en>
  <meta charset=utf-8>
  <title>Hello, world.</title>
</html>

Performance

Unless there's a valid reason for loading your scripts before your content, don't block the rendering of your page.

If your style sheet is heavy, isolate the styles that are absolutely required initially and defer the loading of the secondary declarations in a separate style sheet. Two HTTP requests is significantly slower than one, but the perception of speed is the most important factor.

<!-- bad -->
<!doctype html>
<meta charset=utf-8>
<script src=analytics.js></script>
<title>Hello, world.</title>
<p>...</p>

<!-- good -->
<!doctype html>
<meta charset=utf-8>
<title>Hello, world.</title>
<p>...</p>
<script src=analytics.js></script>
rogerxu commented 6 years ago

CSS

Semicolons

While the semicolon is technically a separator in CSS, always treat it as a terminator.

/* bad */
div {
  color: red
}

/* good */
div {
  color: red;
}

Box model

The box model should ideally be the same for the entire document. A global * { box-sizing: border-box; } is fine, but don't change the default box model on specific elements if you can avoid it.

/* bad */
div {
  width: 100%;
  padding: 10px;
  box-sizing: border-box;
}

/* good */
div {
  padding: 10px;
}

Flow

Don't change the default behavior of an element if you can avoid it. Keep elements in the natural document flow as much as you can. For example, removing the white-space below an image shouldn't make you change its default display:

/* bad */
img {
  display: block;
}

/* good */
img {
  vertical-align: middle;
}

Similarly, don't take an element off the flow if you can avoid it.

/* bad */
div {
  width: 100px;
  position: absolute;
  right: 0;
}

/* good */
div {
  width: 100px;
  margin-left: auto;
}

Positioning

There are many ways to position elements in CSS. Favor modern layout specifications such as Flexbox and Grid, and avoid removing elements from the normal document flow, for example with position: absolute.

Selectors

Minimize selectors tightly coupled to the DOM. Consider adding a class to the elements you want to match when your selector exceeds 3 structural pseudo-classes, descendant or sibling combinators.

/* bad */
div:first-of-type :last-child > p ~ *

/* good */
div:first-of-type .info

Avoid overloading your selectors when you don't need to.

/* bad */
img[src$=svg], ul > li:first-child {
  opacity: 0;
}

/* good */
[src$=svg], ul > :first-child {
  opacity: 0;
}

Specificity

Don't make values and selectors hard to override. Minimize the use of id's and avoid !important.

/* bad */
.bar {
  color: green !important;
}
.foo {
  color: red;
}

/* good */
.foo.bar {
  color: green;
}
.foo {
  color: red;
}

Overriding

Overriding styles makes selectors and debugging harder. Avoid it when possible.

/* bad */
li {
  visibility: hidden;
}
li:first-child {
  visibility: visible;
}

/* good */
li + li {
  visibility: hidden;
}

Inheritance

Don't duplicate style declarations that can be inherited.

/* bad */
div h1, div p {
  text-shadow: 0 1px 0 #fff;
}

/* good */
div {
  text-shadow: 0 1px 0 #fff;
}

Brevity

Keep your code terse. Use shorthand properties and avoid using multiple properties when it's not needed.

/* bad */
div {
  transition: all 1s;
  top: 50%;
  margin-top: -10px;
  padding-top: 5px;
  padding-right: 10px;
  padding-bottom: 20px;
  padding-left: 10px;
}

/* good */
div {
  transition: 1s;
  top: calc(50% - 10px);
  padding: 5px 10px 20px;
}

Language

Prefer English over math.

/* bad */
:nth-child(2n + 1) {
  transform: rotate(360deg);
}

/* good */
:nth-child(odd) {
  transform: rotate(1turn);
}

Vendor prefixes

Kill obsolete vendor prefixes aggressively. If you need to use them, insert them before the standard property.

/* bad */
div {
  transform: scale(2);
  -webkit-transform: scale(2);
  -moz-transform: scale(2);
  -ms-transform: scale(2);
  transition: 1s;
  -webkit-transition: 1s;
  -moz-transition: 1s;
  -ms-transition: 1s;
}

/* good */
div {
  -webkit-transform: scale(2);
  transform: scale(2);
  transition: 1s;
}

Animations

Favor transitions over animations. Avoid animating other properties than opacity and transform.

/* bad */
div:hover {
  animation: move 1s forwards;
}
@keyframes move {
  100% {
    margin-left: 100px;
  }
}

/* good */
div:hover {
  transition: 1s;
  transform: translateX(100px);
}

Units

Use unitless values when you can. Favor rem if you use relative units. Prefer seconds over milliseconds.

/* bad */
div {
  margin: 0px;
  font-size: .9em;
  line-height: 22px;
  transition: 500ms;
}

/* good */
div {
  margin: 0;
  font-size: .9rem;
  line-height: 1.5;
  transition: .5s;
}

Colors

If you need transparency, use rgba. Otherwise, always use the hexadecimal format.

/* bad */
div {
  color: hsl(103, 54%, 43%);
}

/* good */
div {
  color: #5a3;
}

Drawing

Avoid HTTP requests when the resources are easily replicable with CSS.

/* bad */
div::before {
  content: url(white-circle.svg);
}

/* good */
div::before {
  content: "";
  display: block;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
}

Hacks

Don't use them.

/* bad */
div {
  // position: relative;
  transform: translateZ(0);
}

/* good */
div {
  /* position: relative; */
  will-change: transform;
}
rogerxu commented 6 years ago

2 Source file basics

2.1 File name

File names must be all lowercase and may include underscores (_) or dashes (-), but no additional punctuation. Follow the convention that your project uses. Filenames’ extension must be .js.

2.2 File encoding: UTF-8

2.3 Special characters

2.3.1 Whitespace characters

  1. All other whitespace characters in string literals are escaped.
  2. Tab characters are not used for indentation.

2.3.2 Special escape sequences

For any character that has a special escape sequence (\', \", \\, \b, \f, \n, \r, \t, \v), that sequence is used rather than the corresponding numeric escape (e.g \x0a, \u000a, or \u{a}). Legacy octal escapes are never used.

2.3.3 Non-ASCII characters

For the remaining non-ASCII characters, either the actual Unicode character (e.g. ) or the equivalent hex or Unicode escape (e.g. \u221e) is used, depending only on which makes the code easier to read and understand.

// Best: perfectly clear even without a comment.
const units = 'μs';

// Allowed, but there’s no reason to do this.
const units = '\u03bcs'; // 'μs'

// Allowed, but awkward and prone to mistakes.
const units = '\u03bcs'; // Greek letter mu, 's'.

// Poor: the reader has no idea what this is.
const units = '\u03bcs';

// Good: use escapes for non-printable characters, and comment if necessary.
return '\ufeff' + content; // byte order mark
rogerxu commented 6 years ago

3 Source file structure

A source file consists of, in order:

  1. License or copyright information, if present
  2. @fileoverview JSDoc, if present
  3. goog.module statement
  4. goog.require statements
  5. The file’s implementation

Exactly one blank line separates each section that is present, except the file's implementation, which may be preceded by 1 or 2 blank lines.

3.1 License or copyright information, if present

3.2 @fileoverview JSDoc, if present

See 7.5

3.3 goog.module statement

All files must declare exactly one goog.module name on a single line: lines containing a goog.module declaration must not be wrapped, and are therefore an exception to the 80-column limit.

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

3.3.1 Hierarchy

Module namespaces may never be named as a direct child of another module's namespace.

// bad
goog.module('foo.bar');   // 'foo.bar.qux' would be fine, though
goog.module('foo.bar.baz');

3.3.2 goog.setTestOnly

The single goog.module statement may optionally be followed by a call to goog.setTestOnly().

3.3.3 goog.module.declareLegacyNamespace

3.3.4 ES6 Modules

Do not use ES6 modules yet (i.e. the export and import keywords), as their semantics are not yet finalized. Note that this policy will be revisited once the semantics are fully-standard.

3.4 goog.require statements

// bad
const randomName = goog.require('something.else'); // name must match

const {clear, forEach, map} = // don't break lines
    goog.require('goog.array');

function someFunction() {
  const alias = goog.require('my.long.name.alias'); // must be at top level
  // …
}

// good
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} Initializes MyFramework. */
goog.require('my.framework.initialization');

3.4.1 goog.forwardDeclare

goog.forwardDeclare is not needed very often, but is a valuable tool to break circular dependencies or to reference late loaded code. These statements are grouped together and immediately follow any goog.require statements. A goog.forwardDeclare statement must follow the same style rules as a goog.require statement.

3.5 The file’s implementation

The actual implementation follows after all dependency information is declared (separated by at least one blank line).

This may consist of any module-local declarations (constants, variables, classes, functions, etc), as well as any exported symbols.

rogerxu commented 6 years ago

4 Formatting

4.1 Braces

4.1.1 Braces are used for all control structures

Braces are required for all control structures, even if the body contains only a single statement. The first statement of a non-empty block must begin on its own line.

// bad
if (someVeryLongCondition())
  doSomething();

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

// good
if (shortCondition()) return;

4.1.2 Nonempty blocks: K&R style

Braces follow the Kernighan and Ritchie style (Egyptian brackets) for nonempty blocks and block-like constructs:

class InnerClass {
  constructor() {}

  /** @param {number} foo */
  method(foo) {
    if (condition(foo)) {
      try {
        // Note: this might fail.
        something();
      } catch (err) {
        recover();
      }
    }
  }
}

4.1.3 Empty blocks: may be concise

An empty block or block-like construct may be closed immediately after it is opened, with no characters, space, or line break in between (i.e. {}), unless it is a part of a multi-block statement (one that directly contains multiple blocks: if/else or try/catch/finally).

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

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

// good
function doNothing() {}

4.2 Block indentation: +2 spaces

4.2.1 Array literals: optionally "block-like"

Any array literal may optionally be formatted as if it were a “block-like construct.”

// good
const a = [
  0,
  1,
  2,
];

const b =
    [0, 1, 2];

const c = [0, 1, 2];

someMethod(foo, [
  0, 1, 2,
], bar);

4.2.2 Object literals: optionally "block-like"

Any object literal may optionally be formatted as if it were a “block-like construct”.

// good
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 Class literals

Use the extends keyword, but not the @extends JSDoc annotation unless the class extends a templatized type.

// good
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 Function expressions

When declaring an anonymous function in the list of arguments for a function call, the body of the function is indented two spaces more than the preceding indentation depth.

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 statements

As with any other block, the contents of a switch block are indented +2.

An explicit block may be used if required by lexical scoping.

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

  case Animal.JABBERWOCK: {
    handleJabberwock();
    break;
  }

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

4.3 Statements

4.3.1 One statement per line

4.3.2 Semicolons are required

Every statement must be terminated with a semicolon. Relying on automatic semicolon insertion is forbidden.

4.4 Column limit: 80

4.5 Line-wrapping

4.5.1 Where to break

The prime directive of line-wrapping is: prefer to break at a higher syntactic level.

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

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

4.5.2 Indent continuation lines at least +4 spaces

When line-wrapping, each line after the first (each continuation line) is indented at least +4 from the original line, unless it falls under the rules of block indentation.

4.6 Whitespace

4.6.1 Vertical whitespace

  1. Between consecutive methods in a class or object literal
    1. Exception: A blank line between two consecutive properties definitions in an object literal (with no other code between them) is optional. Such blank lines are used as needed to create logical groupings of fields.
  2. Within method bodies, sparingly to create logical groupings of statements. Blank lines at the start or end of a function body are not allowed.
  3. Optionally before the first or after the last method in a class or object literal (neither encouraged nor discouraged).
  4. As required by other sections of this document (e.g. 3.4 goog.require statements).

4.6.2 Horizontal whitespace

Trailing whitespace is forbidden.

  1. Separating any reserved word (such as if, for, or catch) from an open parenthesis (() that follows it on that line.

    // good
    if (something)
  2. Separating any reserved word (such as else or catch) from a closing curly brace (}) that precedes it on that line.

    else {
  3. Before any open curly brace ({), with two exceptions:

    1. Before an object literal that is the first argument of a function or the first element in an array literal.
    `foo({a: [{c: d}]})
    1. In a template expansion, as it is forbidden by the language.
    const str = `abc${1 + 2}def`;
  4. On both sides of any binary or ternary operator.

    const a = isGood ? 'good' : 'bad';
  5. After a comma (,) or semicolon (;). Note that spaces are never allowed before these characters.

    const arr = [1, 2];
  6. After the colon (:) in an object literal.

    const obj = {a: 1, b: 2};
  7. On both sides of the double slash (//) that begins an end-of-line comment. Here, multiple spaces are allowed, but not required.

    const a = 1; // comment
  8. After an open-JSDoc comment character and on both sides of close characters.

    this.foo = /** @type {number} */ (bar);
    
    function(/** string */ foo) {}

4.6.3 Horizontal alignment: discouraged

It is generally discouraged.

4.6.4 Function arguments

Prefer to put all function arguments on the same line as the function name.

To save space, you may wrap as close to 80 as possible, or put each argument on its own line to enhance readability. Indentation should be four spaces.

// 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 Grouping parentheses: recommended

Do not use unnecessary parentheses around the entire expression following delete, typeof, void, return, throw, case, in, of, or yield.

Parentheses are required for type casts.

/** @type {!Foo} */ (foo)

4.8 Comments

4.8.1 Block comment style

Block comments are indented at the same level as the surrounding code. They may be in /* … */ or //-style. For multi-line /* … */ comments, subsequent lines must start with * aligned with the * on the previous line, to make comments obvious with no extra context. “Parameter name” comments should appear after values whenever the value and method name do not sufficiently convey the meaning.

/*
 * This is
 * okay.
 */

// And so
// is this.

/* This is fine, too. */

someFunction(obviousParam, true /* shouldRender */, 'hello' /* name */);
rogerxu commented 6 years ago

5 Language features

5.1 Local variable declarations

5.1.1 Use const and let

Declare all local variables with either const or let. Use const by default, unless a variable needs to be reassigned. The var keyword must not be used.

5.1.2 One variable per declaration

Every local variable declaration declares only one variable.

// bad
let a = 1, b = 2;

5.1.3 Declared when needed, initialized as soon as possible

5.1.4 Declare types as needed

JSDoc type annotations may be added either on the line above the declaration, or else inline before the variable name.

const /** !Array<number> */ data = [];

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

5.2 Array literals

5.2.1 Use trailing commas

const values = [
  'first value',
  'second value',
];

5.2.2 Do not use the variadic Array constructor

// bad
const a1 = new Array(x1, x2, x3);
const a2 = new Array(x1, x2);
const a3 = new Array(x1);
const a4 = new Array();

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

5.2.3 Non-numeric properties

Do not define or use non-numeric properties on an array (other than length). Use a Map (or Object) instead.

5.2.4 Destructuring

Array literals may be used on the left-hand side of an assignment to perform destructuring.

A final rest element may be included (with no space between the ... and the variable name).

// bad
function badDestructuring([a, b] = [4, 2]) { … };

// good
const [a, b, c, ...rest] = generateResults();
let [, b,, d] = someArray;

Destructuring may also be used for function parameters (note that a parameter name is required but ignored). Always specify [] as the default value if a destructured array parameter is optional, and provide default values on the left hand side.

/** @param {!Array<number>=} param1 */
function optionalDestructuring([a = 4, b = 2] = []) { … };

5.2.5 Spread operator

Array literals may include the spread operator (...) to flatten elements out of one or more other iterables. The spread operator should be used instead of more awkward constructs with Array.prototype. There is no space after the ....

[...foo]   // preferred over Array.prototype.slice.call(foo)
[...foo, ...bar]   // preferred over foo.concat(bar)

5.3 Object literals

5.3.1 Use trailing commas

5.3.2 Do not use the Object constructor

5.3.3 Do not mix quoted and unquoted keys

5.3.4 Computed property names

const obj = {['key' + foo()]: 42};

5.3.5 Method shorthand

return {
  stuff: 'candy',
  method() {
    return this.stuff;  // Returns 'candy'
  },
};

5.3.6 Shorthand properties

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

5.3.7 Destructuring

Destructured objects may also be used as function parameters, but should be kept as simple as possible: a single level of unquoted shorthand properties.

Specify any default values in the left-hand-side of the destructured parameter ({str = 'some default'} = {}, rather than {str} = {str: 'some default'}).

If a destructured object is itself optional, it must default to {}.

// bad
/** @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}) {};

// good
/**
 * @param {string} ordinary
 * @param {{num: (number|undefined), str: (string|undefined)}=} param1
 *     num: The number of times to do something.
 *     str: A string to do stuff to.
 */
function destructured(ordinary, {num, str = 'some default'} = {})

5.3.8 Enums

Enumerations are defined by adding the @enum annotation to an object literal. Additional properties may not be added to an enum after it is defined. Enums must be constant, and all enum values must be deeply immutable.

/**
 * Supported temperature scales.
 * @enum {string}
 */
const TemperatureScale = {
  CELSIUS: 'celsius',
  FAHRENHEIT: 'fahrenheit',
};

/**
 * An enum with two options.
 * @enum {number}
 */
const Option = {
  /** The option used shall have been the first. */
  FIRST_OPTION: 1,
  /** The second among two options. */
  SECOND_OPTION: 2,
};

5.4 Classes

5.4.1 Constructors

5.4.2 Fields

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

5.4.3 Computed properties

Computed properties may only be used in classes when the property is a symbol. A [Symbol.iterator] method should be defined for any classes that are logically iterable. Beyond this, Symbol should be used sparingly.

5.4.4 Static methods

Where it does not interfere with readability, prefer module-local functions over private static methods.

// bad
class Base { /** @nocollapse */ static foo() {} }
class Sub extends Base {}
function callFoo(cls) { cls.foo(); }  // discouraged: don't call static methods dynamically
Sub.foo();  // illegal: don't call static methods on subclasses that don't define it themselves

5.4.5 Old-style class declarations

While ES6 classes are preferred, there are cases where ES6 classes may not be feasible.

  1. If there exist or will exist subclasses, including frameworks that create subclasses, that cannot be immediately changed to use ES6 class syntax. If such a class were to use ES6 syntax, all downstream subclasses not using ES6 class syntax would need to be modified.

  2. Frameworks that require a known this value before calling the superclass constructor, since constructors with ES6 super classes do not have access to the instance this value until the call to super returns.

5.4.6 Do not manipulate prototypes directly

The class keyword allows clearer and more readable class definitions than defining prototype properties.

5.4.7 Getters and Setters

Do not use JavaScript getter and setter properties. They are potentially surprising and difficult to reason about, and have limited support in the compiler. Provide ordinary methods instead.

Getters must not change observable state.

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

5.4.8 Overriding toString

The toString method may be overridden, but must always succeed and never have visible side effects.

Beware, in particular, of calling other methods from toString, since exceptional conditions could lead to infinite loops.

5.4.9 Interfaces

Interfaces may be declared with @interface or @record. Interfaces declared with @record can be explicitly (i.e. via @implements) or implicitly implemented by a class or object literal.

All non-static method bodies on an interface must be empty blocks. Fields must be defined after the interface body as stubs on the prototype.

**
 * Something that can frobnicate.
 * @record
 */
class Frobnicator {
  /**
   * Performs the frobnication according to the given strategy.
   * @param {!FrobnicationStrategy} strategy
   */
  frobnicate(strategy) {}
}

/** @type {number} The number of attempts before giving up. */
Frobnicator.prototype.attempts;

5.5 Functions

5.5.1 Top-level functions

Exported functions may be defined directly on the exports object, or else declared locally and exported separately.

Non-exported functions are encouraged and should not be declared @private.

/** @return {number} */
function helperFunction() {
  return 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) => {
  // do some stuff ...
};

5.5.2 Nested functions and closures

Functions may contain nested function definitions. If it is useful to give the function a name, it should be assigned to a local const.

5.5.3 Arrow functions

Arrow functions provide a concise syntax and fix a number of difficulties with this. Prefer arrow functions over the function keyword, particularly for nested functions.

Prefer using arrow functions over f.bind(this). Avoid writing const self = this. Arrow functions are particularly useful for callbacks, which sometimes pass unexpected additional arguments.

The right-hand side of the arrow may be a single expression or a block. Parentheses around the arguments are optional if there is only a single non-destructured argument. It is a good practice to use parentheses even for single-argument arrows, since the code may still parse reasonably (but incorrectly) if the parentheses are forgotten when an additional argument is added.

5.5.4 Generators

Generators enable a number of useful abstractions and may be used as needed.

When defining generator functions, attach the * to the function keyword when present, and separate it with a space from the name of the function. When using delegating yields, attach the * to the yield keyword.

/** @return {!Iterator<number>} */
function* gen1() {
  yield 42;
}

/** @return {!Iterator<number>} */
const gen2 = function*() {
  yield* gen1();
}

class SomeClass {
  /** @return {!Iterator<number>} */
  * gen() {
    yield 42;
  }
}

5.5.5 Parameters

Function parameters must be typed with JSDoc annotations in the JSDoc preceding the function’s definition, except in the case of same-signature @override, where all types are omitted.

Parameter types may be specified inline, immediately before the parameter name.

(/** number */ foo, /** string */ bar) => foo + bar

Inline and @param type annotations must not be mixed in the same function definition.

5.5.5.1 Default parameters
/**
 * @param {string} required This parameter is always needed.
 * @param {string=} optional This parameter can be omitted.
 * @param {!Node=} node Another optional parameter.
 */
function maybeDoSomething(required, optional = '', node = undefined) {}

Use default parameters sparingly. Prefer destructuring to create readable APIs when there are more than a small handful of optional parameters that do not have a natural order.

5.5.5.2 Rest parameters

Use a rest parameter instead of accessing arguments. Rest parameters are typed with a ... prefix in their JSDoc. The rest parameter must be the last parameter in the list. There is no space between the ... and the parameter name. Do not name the rest parameter var_args. Never name a local variable or parameter arguments, which confusingly shadows the built-in name.

/**
 * @param {!Array<string>} array This is an ordinary parameter.
 * @param {...number} numbers The remainder of arguments are all numbers.
 */
function variadic(array, ...numbers) {}

5.5.6 Returns

Function return types must be specified in the JSDoc directly above the function definition, except in the case of same-signature @override where all types are omitted.

5.5.7 Generics

Declare generic functions and methods when necessary with @template TYPE in the JSDoc above the class definition.

5.5.8 Spread operator

Function calls may use the spread operator (...). Prefer the spread operator to Function.prototype.apply when an array or iterable is unpacked into multiple parameters of a variadic function. There is no space after the ....

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

5.6 String literals

5.6.1 Use single quotes

Ordinary string literals may not span multiple lines.

5.6.2 Template strings

Template strings may span multiple lines.

If a template string spans multiple lines, it does not need to follow the indentation of the enclosing block, though it may if the added whitespace does not matter.

function arithmetic(a, b) {
  return `Here is a table of arithmetic operations:
${a} + ${b} = ${a + b}
${a} - ${b} = ${a - b}
${a} * ${b} = ${a * b}
${a} / ${b} = ${a / b}`;
}

5.6.3 No line continuations

Do not use line continuations (that is, ending a line inside a string literal with a backslash) in either ordinary or template string literals. Even though ES5 allows this, it can lead to tricky errors if any trailing whitespace comes after the slash, and is less obvious to readers.

// bad
const longString = 'This is a very long string that far exceeds the 80 \
    column limit. It unfortunately contains long stretches of spaces due \
    to how the continued lines are indented.';

// good
const longString = 'This is a very long string that far exceeds the 80 ' +
    'column limit. It does not contain long stretches of spaces since ' +
    'the concatenated strings are cleaner.';

5.7 Number literals

Numbers may be specified in decimal, hex, octal, or binary. Use exactly 0x, 0o, and 0b prefixes, with lowercase letters, for hex, octal, and binary, respectively. Never include a leading zero unless it is immediately followed by x, o, or b.

5.8 Control structures

5.8.1 For loops

With ES6, the language now has three different kinds of for loops. All may be used, though for-of loops should be preferred when possible.

5.8.2 Exceptions

Always throw Errors or subclasses of Error: never throw string literals or other objects. Always use new when constructing an Error.

Custom exceptions provide a great way to convey additional error information from functions. They should be defined and used wherever the native Error type is insufficient.

Prefer throwing exceptions over ad-hoc error-handling approaches (such as passing an error container reference type, or returning an object with an error property).

5.8.2.1 Empty catch blocks

It is very rarely correct to do nothing in response to a caught exception. When it truly is appropriate to take no action whatsoever in a catch block, the reason this is justified is explained in a comment.

// bad
try {
  shouldFail();
  fail('expected an error');
} catch (expected) {}

// good
try {
  return handleNumericResponse(response);
} catch (ok) {
  // it's not numeric; that's fine, just continue
}
return handleTextResponse(response);

5.8.3 Switch statements

5.8.3.1 Fall-through: commented

Any comment that communicates the idea of fall-through is sufficient (typically // fall through). This special comment is not required in the last statement group of the switch block.

switch (input) {
  case 1:
  case 2:
    prepareOneOrTwo();
  // fall through
  case 3:
    handleOneTwoOrThree();
    break;
  default:
    handleLargeNumber(input);
}
5.8.3.2 The default case is present

Each switch statement includes a default statement group, even if it contains no code.

5.9 this

Only use this in class constructors and methods, or in arrow functions defined within class constructors and methods. Any other uses of this must have an explicit @this declared in the immediately-enclosing function’s JSDoc.

Never use this to refer to the global object, the context of an eval, the target of an event, or unnecessarily call()ed or apply()ed functions.

5.10 Disallowed features

5.10.1 with

Do not use the with keyword. It makes your code harder to understand and has been banned in strict mode since ES5.

5.10.2 Dynamic code evaluation

Do not use eval or the Function(...string) constructor (except for code loaders). These features are potentially dangerous and simply do not work in CSP environments.

5.10.3 Automatic semicolon insertion

Always terminate statements with semicolons.

5.10.4 Non-standard features

Do not use non-standard features. This includes old features that have been removed (e.g., WeakMap.clear), new features that are not yet standardized (e.g., the current TC39 working draft, proposals at any stage, or proposed but not-yet-complete web standards), or proprietary features that are only implemented in some browsers.

5.10.5 Wrapper objects for primitive types

Never use new on the primitive object wrappers (Boolean, Number, String, Symbol), nor include them in type annotations.

// bad
const /** Boolean */ x = new Boolean(false);
if (x) alert(typeof x);  // alerts 'object' - WAT?

// good
const /** boolean */ x = Boolean(0);
if (!x) alert(typeof x);  // alerts 'boolean', as expected

5.10.6 Modifying builtin objects

Never modify builtin types, either by adding methods to their constructors or to their prototypes. Avoid depending on libraries that do this.

Do not add symbols to the global object unless absolutely necessary (e.g. required by a third-party API).

rogerxu commented 6 years ago

6 Naming

6.1 Rules common to all identifiers

Give as descriptive a name as possible, within reason. Do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word.

// bad
n                     // Meaningless.
nErr                  // Ambiguous abbreviation.
nCompConns            // Ambiguous abbreviation.
wgcConnections        // Only your group knows what this stands for.
pcReader              // Lots of things can be abbreviated "pc".
cstmrId               // Deletes internal letters.
kSecondsPerDay        // Do not use Hungarian notation.

// good
priceCountReader      // No abbreviation.
numErrors             // "num" is a widespread convention.
numDnsConnections     // Most people know what "DNS" stands for.

6.2 Rules by identifier type

6.2.1 Package names

Package names are all lowerCamelCase. For example, my.exampleCode.deepSpace, but not my.examplecode.deepspace or my.example_code.deep_space.

6.2.2 Class names

Class, interface, record, and typedef names are written in UpperCamelCase. Unexported classes are simply locals: they are not marked @private and therefore are not named with a trailing underscore.

Type names are typically nouns or noun phrases. For example, Request, ImmutableList, or VisibilityMode. Additionally, interface names may sometimes be adjectives or adjective phrases instead (for example, Readable).

6.2.3 Method names

Method names are written in lowerCamelCase. Private methods’ names must end with a trailing underscore.

Method names are typically verbs or verb phrases. For example, sendMessage or stop_. Getter and setter methods for properties are never required, but if they are used they should be named getFoo (or optionally isFoo or hasFoo for booleans), or setFoo(value) for setters.

Underscores may also appear in JsUnit test method names to separate logical components of the name. One typical pattern is test<MethodUnderTest>_<state>, for example testPop_emptyStack. There is no One Correct Way to name test methods.

6.2.4 Enum names

Enum names are written in UpperCamelCase, similar to classes, and should generally be singular nouns. Individual items within the enum are named in CONSTANT_CASE.

6.2.5 Constant names

Constant names use CONSTANT_CASE: all uppercase letters, with words separated by underscores. There is no reason for a constant to be named with a trailing underscore, since private static properties can be replaced by (implicitly private) module locals.

Every constant is a @const static property or a module-local const declaration, but not all @const static properties and module-local consts are constants. Before choosing constant case, consider whether the field really feels like a deeply immutable constant.

// Constants
const NUMBER = 5;
/** @const */ exports.NAMES = ImmutableList.of('Ed', 'Ann');
/** @enum */ exports.SomeEnum = { ENUM_CONSTANT: 'value' };

// Not constants
let letVariable = 'non-const';
class MyClass { constructor() { /** @const */ this.nonStatic = 'non-static'; } };
/** @type {string} */ MyClass.staticButMutable = 'not @const, can be reassigned';
const /** Set<String> */ mutableCollection = new Set();
const /** ImmutableSet<SomeMutableType> */ mutableElements = ImmutableSet.of(mutable);
const Foo = goog.require('my.Foo');  // mirrors imported name
const logger = log.getLogger('loggers.are.not.immutable');
6.2.5.1 Local aliases

Local aliases should be used whenever they improve readability over fully-qualified names. Aliases may also be used within functions. Aliases must be const.

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

6.2.6 Non-constant field names

Non-constant field names (static or otherwise) are written in lowerCamelCase, with a trailing underscore for private fields.

These names are typically nouns or noun phrases. For example, computedValues or index_.

6.2.7 Parameter names

Parameter names are written in lowerCamelCase. Note that this applies even if the parameter expects a constructor.

One-character parameter names should not be used in public methods.

6.2.8 Local variable names

Local variable names are written in lowerCamelCase, except for module-local (top-level) constants, as described above. Constants in function scopes are still named in lowerCamelCase.

6.2.9 Template parameter names

Template parameter names should be concise, single-word or single-letter identifiers, and must be all-caps, such as TYPE or THIS.

6.3 Camel case: defined

Note: This is different than Airbnb guide.

XMLHTTPRequest // bad
XmlHttpRequest // good

newCustomerID // bad
newCustomerId // good

innerStopWatch // bad
innerStopwatch // good

supportsIPv6OnIOS // bad
supportsIpv6OnIos // good

YoutubeImporter // bad
YouTubeImporter // good
rogerxu commented 6 years ago

7 JSDoc

7.1 General form

/**
 * 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;

7.2 Markdown

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

7.3 JSDoc tags

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

// good
/**
 * 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];
  }
}

7.4 Line wrapping

Line-wrapped block tags are indented four spaces. Wrapped description text may be lined up with the description on previous lines, but this horizontal alignment is discouraged.

/**
 * 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;
};

7.5 Top/file-level comments

A file may have a top-level file overview. A copyright notice, author information, and default visibility level are optional. File overviews are generally recommended whenever a file consists of more than a single class definition. The top level comment is designed to orient readers unfamiliar with the code to what is in this file. If present, it may provide a description of the file's contents and any dependencies or compatibility information. Wrapped lines are not indented.

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

7.6 Class comments

Classes, interfaces and records must be documented with a description and any template parameters, implemented interfaces, visibility, or other appropriate tags. The class description should provide the reader with enough information to know how and when to use the class, as well as any additional considerations necessary to correctly use the class. Textual descriptions may be omitted on the constructor. @constructor and @extends annotations are not used with the class keyword unless the class is being used to declare an @interface or it extends a generic class.

/**
 * 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 and typedef comments

Enums and typedefs must be documented. Public enums and typedefs must have a non-empty description. Individual enum items may be documented with a JSDoc comment on the preceding line.

/**
 * 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',
};

7.8 Method and function comments

/** This is a class. */
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) { ... }

Anonymous functions do not require JSDoc, though parameter types may be specified inline if the automatic type inference is insufficient.

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

7.9 Property comments

Property types must be documented. The description may be omitted for private properties, if name and type provide enough documentation for understanding the code.

Publicly exported constants are commented the same way as properties. Explicit types may be omitted for @const properties initialized from an expression with an obviously known type.

/** My class. */
class MyClass {
  /** @param {string=} someString */
  constructor(someString = 'default string') {
    /** @private @const */
    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
 */
MyClass.RETRY_COUNT = 33;

7.10 Type annotations

Type annotations are found on @param, @return, @this, and @type tags, and optionally on @const, @export, and any visibility tags. Type annotations attached to JSDoc tags must always be enclosed in braces.

7.10.1 Nullability

The type system defines modifiers ! and ? for non-null and nullable, respectively. Primitive types (undefined, string, number, boolean, symbol, and function(...): ...) and record literals ({foo: string, bar: number}) are non-null by default. Do not add an explicit ! to these types. Object types (Array, Element, MyClass, etc) are nullable by default, but cannot be immediately distinguished from a name that is @typedef’d to a non-null-by-default type. As such, all types except primitives and record literals must be annotated explicitly with either ? or ! to indicate whether they are nullable or not.

7.10.2 Type Casts

In cases where type checking doesn't accurately infer the type of an expression, it is possible to tighten the type by adding a type annotation comment and enclosing the expression in parentheses. Note that the parentheses are required.

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

7.10.3 Template Parameter Types

Always specify template parameters. This way compiler can do a better job and it makes it easier for readers to understand what code does.

// bad
const /** !Object */ users = {};
const /** !Array */ books = [];
const /** !Promise */ response = ...;

// good
const /** !Object<string, !User> */ users = {};
const /** !Array<string> */ books = [];
const /** !Promise<!Response> */ response = ...;

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

7.11 Visibility annotations

Visibility annotations (@private, @package, @protected) may be specified in a @fileoverview block, or on any exported symbol or property. Do not specify visibility for local variables, whether within a function or at the top level of a module. All @private names must end with an underscore.

rogerxu commented 6 years ago

8 Policies

8.1 Issues unspecified by Google Style: Be Consistent!

8.2 Compiler warnings

8.2.1 Use a standard warning set

As far as possible projects should use --warning_level=VERBOSE.

8.2.2 How to handle a warning

Before doing anything, make sure you understand exactly what the warning is telling you.

Once you understand the warning, attempt the following solutions in order:

  1. First, fix it or work around it. Make a strong attempt to actually address the warning, or find another way to accomplish the task that avoids the situation entirely.
  2. Otherwise, determine if it's a false alarm. If you are convinced that the warning is invalid and that the code is actually safe and correct, add a comment to convince the reader of this fact and apply the @suppress annotation.
  3. Otherwise, leave a TODO comment. This is a last resort. If you do this, do not suppress the warning. The warning should be visible until it can be taken care of properly.

8.2.3 Suppress a warning at the narrowest reasonable scope

Warnings are suppressed at the narrowest reasonable scope, usually that of a single local variable or very small method. Often a variable or method is extracted for that reason alone.

/** @suppress {uselessCode} Unrecognized 'use asm' declaration */
function fn() {
  'use asm';
  return 0;
}

Even a large number of suppressions in a class is still better than blinding the entire class to this type of warning.

8.3 Deprecation

Mark deprecated methods, classes or interfaces with @deprecated annotations. A deprecation comment must include simple, clear directions for people to fix their call sites.

8.4 Code not in Google Style

You will occasionally encounter files in your codebase that are not in proper Google Style. These may have come from an acquisition, or may have been written before Google Style took a position on some issue, or may be in non-Google Style for any other reason.

8.4.1 Reformatting existing code

When updating the style of existing code, follow these guidelines.

  1. It is not required to change all existing code to meet current style guidelines. Reformatting existing code is a trade-off between code churn and consistency. Style rules evolve over time and these kinds of tweaks to maintain compliance would create unnecessary churn. However, if significant changes are being made to a file it is expected that the file will be in Google Style.
  2. Be careful not to allow opportunistic style fixes to muddle the focus of a CL. If you find yourself making a lot of style changes that aren’t critical to the central focus of a CL, promote those changes to a separate CL.

8.4.2 Newly added code: use Google Style

Brand new files use Google Style, regardless of the style choices of other files in the same package.

When adding new code to a file that is not in Google Style, reformatting the existing code first is recommended.

If this reformatting is not done, then new code should be as consistent as possible with existing code in the same file, but must not violate the style guide.

8.5 Local style rules

eams and projects may adopt additional style rules beyond those in this document, but must accept that cleanup changes may not abide by these additional rules, and must not block such cleanup changes due to violating any additional rules. Beware of excessive rules which serve no purpose. The style guide does not seek to define style in every possible scenario and neither should you.

8.6 Generated code: mostly exempt

Source code generated by the build process is not required to be in Google Style. However, any generated identifiers that will be referenced from hand-written source code must follow the naming requirements. As a special exception, such identifiers are allowed to contain underscores, which may help to avoid conflicts with hand-written identifiers.