Javascript

These are the Javascript-specific coding styles. Anything mentions here overrides/adds to the "In General" guides. The only 1 principle when coding Javascript is that:

  • Be consistent. As long as it is sharing a single pattern, it is maintainable.

#1 Extension should only be .js

  • Use underscore (_) and dash (-) instead of space ( ).
  • Extension should be .js only.


Rationale

  • Standardization with extension.
  • Do you really need a reason to argue for using underscore and dash?

#2 Use UTF-8

  • Use only UTF-8 characters complying to HTML5.


Rationale

  • Security reason.
  • Compliance to HTML5 codes.

#3 Use Normal Escape Sequences

  • Use \', \", \\, \b, \f, \n, \r, \t, \v, and etc.
  • DO NOT use their numeric format (e.g \x0a, \u000a, or \u{a}).
  • DO NOT use legacy octal escapes.


Rationale

  • Readability purpose.

#4 - Non-ASCII Characters

  • Use the uni-code character instead.
  • DO NOT use numeric format with explanation comment.

Example:

/* BEST */
const units = 'μs';

/* BAD: what's the point? */
const units = '\u03bcs'; // 'μs'

/* WORSE: what's the point at all? You're using UTF-8. */
const units = '\u03bcs'; // Greek letter mu, 's'

/* WORST: compromising readability by making people guessing. */
const units = '\u03bcs';

/* GOOD: use escapes for non-printable characters, and comment if necessary. */
return '\ufeff' + content; // byte order mark


Rationale

  • Readability purpose. Javascipt is already a horrible language. DO NOT Make it worse.

#5 - Source File Header

  • All source file should have the header of the following sequence:
    • License and Copyright Information if present
    • @fileoverview JSDoc, if present
    • Any module/plugin/source codes statements, if present
    • file's implementations

Example:

/** Copyright (C) Chew Kean Ho, Inc - All Rights Reserved
 * Unauthorized copying of this file, via any medium is strictly prohibited
 * Proprietary and confidential
 * Written by Chew Kean Ho <hollowaykeanho@gmail.com>, September 2018
 *
 * @fileOverview Custom code for the Candidates List page.
 * Program flow on page load:
 * - customSearchInit()
 *   - initSearchPage()
 *     - populateDropdownFields()
 *     - LoadAdditionalListDataAndContinue()
 *       - loadListData()
 *         - generateHtmlTable()
 *         - finishAsync()
 *           - completePageLoad()
 */


Rationale

  • Clarity of use, especially on a widely deployed codes such as Javascript.

#6 - Use Braces for All Control Statements

  • Comply to any other languages even-though Javascript is lax about it.
  • The 1st statement on a non-empty block must be on a new indented line.
    • Exception: when the condition is returning and is a single line doing nothing.

Not recommended:

if (someVeryLongCondition())
  doSomething();

if (shortCondition())
  return;

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

Recommended:

if (someVeryLongCondition()) doSomething();

if (shortCondition()) return;

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

#7 - Non Empty Block (K & R)

  • No line break before the opening brace.
  • Line break after the opening brace.
  • Line break before the closing brace.
  • Line break after the closing brace if that brace terminates a statement or the body of a function or class statement, or a class method. Specifically, there is no line break after the brace if it is followed by else, catch, while, or a comma, semicolon, or right-parenthesis.
class InnerClass {
  constructor() {}

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

#8 - Empty Block

  • Be consistent with block formatting.
  • An empty block or block-like construct may be closed immediately after it is opened, with no characters, space, or line break in between braces {}.
    • Exception to multiple conditions.


Not Recommended:

function doNothing() {
  // do nothing
}

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

try {
  return handleNumericResponse(response);
} catch (ok) {}
return handleTextResponse(response);


Recommended:

function doNothing() {}

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

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


Rationale

  • Consistency for readability.
  • Minification will strip the spacing and commenting off anyway. Hence, write for reader and minification compiler.

#9 - Indentation

  • 2 space Tab


Rationale

  • Minification will strip the spacing and commenting off anyway. Hence, write for reader and minification compiler.

#10 - Array and Object Literals

  • Expressible in many ways but use common sense and avoid sacrificing readability.
  • Use trailing comma.
  • DO NOT use variadic declaration.
  • DO NOT use non-numeric properties on array. Use Map instead.

Some examples of array:

const a = [0, 1, 2];

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

const c =
    [0, 1, 2];

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

Some examples of object literals:

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

DO NOT:

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

DO:

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

#11 - Class Literals

  • Indented by blocks.
  • use extends keyword not JSDoc's @extends unless the class is a template type.
class Foo {
  constructor() {
    /** @type {number} */
    this.x = 42;
  }

  /** @return {number} */
  method() {
    return this.x;
  }
}
Foo.Empty = class {};


Inheritance example:

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

/** @interface */
class Frobnicator {
  /** @param {string} message */
  frobnicate(message) {}
}

#12 - Function Expression

  • Keep the indentation aligned.
  • Avoid massive concatenation. Split into multiple lines when needed.
  • Preferbly arugment on the same line with function. Split the arguments list into separate lines in readable way.
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();
      }
    });


// 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) {
  // …
}

#13 - Switch Statement

  • Comply back to the standards in "In General" section.
switch (animal) {
case Animal.BANDERSNATCH:
  handleBandersnatch();
  break;
case Animal.JABBERWOCK:
  handleJabberwock();
  break;
default:
  throw new Error('Unknown animal');
}

#14 - Discourage Horizontal Alignment

  • Leave it crude.
{
  tiny: 42, // this is great
  longer: 435, // this too
};

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


Rationale

  • Most linter / formatting / minimalization process is going to mess it up anyway. Why waste time?

#15 - Comments

  • Comply to the In General Comment section.
  • Use comments rationally.
  • Comply to JDocs if available.


/*
 * This is
 * okay.
 */

// And so
// is this.

/* This is fine, too. */

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

#16 - Use Local Variables

  • Set all variables with const or let declaration.
  • 1 variable per declaration.
  • Declare as needed; initialize as soon as possible.
  • Comment the type if needed.
/** @type integer */
let a = 2

/** @type integer */ 
let b = 3

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

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

#17 - No Line Continuation

  • Javascript does not facilitate line continuation. Use concatenation instead.

ILLEGAL:

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.';

CORRECT:

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.';

#18 - Only Use this In Class Constructor or Methods

  • Never use this to refer global object.

#19 - Forbid and Be Discipline

  • DO NOT use with.
  • DO NOT use eval.
  • DO NOT use Function(...string)
  • ALWAYS terminate line with semi-colon.
  • DO NOT use non-standard features.
  • DO NOT Wrap object for primitive types.
  • DO NOT modify built-in objects.

BAD:

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

DO:

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

#20 - Naming

  • Use only ASCII and digits for name.
  • Use CamelCase.
  • Package name is all lowerCamelCase.
  • Class name is all UpperCamelCase.
  • Method name is all lowerCamelCase.
  • Enum name is UpperCamelCase.
  • Constant name is UPPERCASE_WITH_UNDERSCORE.
  • non-constant field name is lowerCamelCase.
  • Parameter name is lowerCamelCase.
  • Local variable name is lowerCamelCase.
  • CamelCase follows this definition:
    • XmlHttpRequest, not XMLHTTPRequest
    • newCustomerId, not newCustomerID
    • innerStopwatch, not innerStopWatch
    • supportsIpv6OnIos, not supportsIPv6OnIOS
    • YouTubeImporter, not YoutubeImporter

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.

my.examplecode.deepspace    // bad package name
my.example_code.deep_space  // bad package name

DO:

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

my.exampleCode.deepSpace    // good package name

#21 - Documentations

#22 - Handling Warning

  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.

There are more Javascript practices but these should be suffice to keep the train going. For more information, you can read:

That's all about Javascript.