Javascript Coding Standards

These coding standards involve the following sections:

A. Code layout

This code layout standard is designed to be generally compatible with the Meteor Style Guide, but is defined in terms of IntelliJ layout settings so that the Code | Reformat Code... command will automatically bring a Javascript file into compliance.

A1. Tabs and indentation

In Preferences | Editor | Code Style, set tab size to 2, indent to 2, continuation indent to 4, and make sure “Use Tab Character” is not checked. (No tab characters.)

Now expand that option to navigate to Preferences | Editor | Code Style | Javascript, and make sure that the same tab and indentation options are set for Javascript:

While you’re at it, set these tabs and indents for CSS, HTML, JSON, and XML, as we will be using these in the class.

A2. Spaces

The default values provided by IntelliJ for Javascript will be the standard. Here is an example:

A3. Wrapping and braces

Check the box so that ‘else’ appears on a new line. Otherwise, the default values are the standard:

A4. Blank lines

Change the maximum blank lines to 1:

A5. Use soft wraps

To facilitate writing HTML, enable soft wraps in Preferences | Editor | General:

B. Naming conventions

B1. Use camelCase for identifiers

Functions should be named like doAThing, not do_a_thing.

Other variable names get the same treatment: isConnected, not is_connected.

B2. Private identifiers start with underscores

If a method or property name starts with _, it’s a private implementation detail and shouldn’t be used from outside the module where it is defined. It could disappear or change at any time.

B3. Don’t use Javascript keywords as names

Do not name a variable or function with a Javascript reserved word.

For example, don’t name a method ‘return’, even if that’s allowed by the Javascript grammar. Some syntax highlighting modes will choke on that. Even if that doesn’t happen, it’s still grating to see it highlighted as a keyword. What’s more, old browsers may fail to parse the code!

B4. Capitalize functions when they are constructors

Normally, functions begin with a lower case letter. In situations where a function is intended to be used with new in order to create an object, the function should begin with an upper case letter.

C. Comments

C1. Use JSDoc commenting conventions.

Use JSDoc to document all function and variable declarations. See Creating JSDoc Comments for IntelliJ support for JSDoc.

Functions should begin with a comment explaining what they do, what arguments they take, and what they return. A good function comment allows developers to use a function without having to read its source.

For example:

/**
 * A Whirlygig represents a remote client's view of the global Weasel.
 * @param name The remote weasel's name.
 * @constructor
 */
var Whirlygig = function (name) {
  var self = this;
  self.name = name;  // name of the remote weasel
  self.values = {};  // remote key name -> 0-indexed value

};

_.extend(Whirlygig.prototype, {

  /**
   * Take a key/value pair from the remote Weasel and save it locally.
   * @param x The key/value pair.
   */
  addValue: function (x) {
    // Weasels use 1-indexed arrays. Subtract 1 to convert to 0-indexed.
    self.values[x.key] = x.value - 1;
  },

  /**
   * Return a list of stored values in a format suitable for sending to a Weasel.
   * @returns {Array}
   */
  serialize: function () {
    return _.map(self.values, function (v, k) {
      var newVal = mungeValue(v, false /* foldValue */);
      // Weasels use 1-indexed arrays. Add 1 to convert back to 1-indexed.
      newVal = newVal + 1;
      return {key: k, value: newVal};
    });
  }
});

C2. Signature comment for arguments

If a function takes more arguments than there are formal parameters (via arguments), comment like so:

_.extend(Foo.prototype, {
  myVariadicFunction: function(/* arguments */) { ... },
  anotherVariadicFunction: function(x, y, z, /* arguments */) { ... },
});

(Not /* varargs */ – that’s a C concept.)

D. Defensive coding.

D1. Use braces even with single-statement blocks

Always use braces in conditionals and loops, even if the body is a single line:

if (!alreadyDone) {
  doSomething();
}

D2. Always use semicolons.

When breaking lines you must be careful to avoid the JavaScript semicolon insertion feature. For example, this code:

return
  doSomething() * 2 + 27;

will be interpreted in JavaScript as:

return;
  doSomething() * 2 + 27;

D3. Equality Testing

Always use ===, not ==.

If you want to compare a number to a string version of said number, use x === parseInt(y), x.toString() === y, or x === +y. It is much more clear what is going on. (Note that those alternatives differ in how they handle non-numeric characters or leading zeros in the string. Only the third alternative gets all the edge cases right.)

D4. Keyword arguments with options

If a function takes a lot of arguments, especially if many of those arguments are optional, or several of the arguments are functions, don’t try to put all of the arguments in the function’s signature. Instead, add a final argument, options.

/**
 * Create a cube
 * @param owner The owner of the cube.
 * @param options Color, weight, material, onRender, and onDestroy.
 */
var makeCube = function (owner, options) {
  // Provide default values for the options.
  options = _.extend({
    color: 'grey',
    weight: 1.0,
    material: 'aluminum',
    onRender: function () {},
    onDestroy: function () {}
  }, options);

console.log(owner + ", here is your " + options.color + "cube.");
options.onRender();
      ...
});

Keep the most important arguments in the signature (eg, owner above). Ideally, the arguments in the signature are the “cake” (the main substance and structure of the operation), and the options are the “icing” (frills and modifiers).

D5. Functions should be consistent about whether they return a value

Either: all returns in a function should either explicitly return a value (perhaps undefined), and function should end with an explicit return or throw; or there should be no return’s in the function which return a value.

var BAD1 = function () {
  if (x)
    return 5;
};
var BAD2 = function () {
  if (x)
    return;
  return 5;
};
var GOOD1 = function () {
  if (x)
    return 5;
  throw Error("Nope");
};
var GOOD2 = function () {
  if (x)
    return 5;
  return undefined;
};

E. Module definition

Javascript module definition standards are in a state of flux: on the server side, the historical defacto standard is CommonJS, on the browser side, the traditional defacto standard is RequireJS, and there have been various efforts to bridge the gap Browserify.

Adding to the current confusion (but hopefully reducing it in the long term) are the modularity constructs available in the upcoming Javascript language release ES6.

For the time being, our coding standard will not take a position on modularity, other than to say that when it does take a position, the position will be to use ES6 modules.

F. JSHint

We will use JSHint to automate certain quality assurance checks.

IntelliJ provides built-in support for JSHint. You will need to enable it for your project. First, download the class standard jshintrc file. Rename to .jshintrc and place in your project’s top-level directory.

Each time you create a project, enable JSHint in IntelliJ and tell it to use your project-level .jshintrc file in Preferences | Languages and Frameworks | Code Quality Tools | JSHint:

Now, when writing Javascript code, you will get error bars along the right margin when you violate a JSHint standard, and the first character of the code violating a rule will have a red underline. Mousing over it will display the problem:

There are two common situations which will require you to add “directives” (i.e. special comments) to your file:

G. IntelliJ Inspections

IntelliJ comes with a set of “Inspections” for a wide variety of languages and environments in Preferences | Editor | Inspections:

Use of these inspections are optional. I tend to disable the “Spelling” inspection for Javascript code.