These coding standards involve the following sections:
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.
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.
The default values provided by IntelliJ for Javascript will be the standard. Here is an example:
Check the box so that ‘else’ appears on a new line. Otherwise, the default values are the standard:
Change the maximum blank lines to 1:
To facilitate writing HTML, enable soft wraps in Preferences | Editor | General
:
Functions should be named like doAThing
, not do_a_thing
.
Other variable names get the same treatment: isConnected
, not is_connected
.
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.
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!
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.
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};
});
}
});
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.)
Always use braces in conditionals and loops, even if the body is a single line:
if (!alreadyDone) {
doSomething();
}
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;
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.)
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).
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;
};
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.
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:
Use of variables and functions defined elsewhere. If you are using Underscore functions, for example, JSHint will complain that “_” is not defined. To fix this kind of error, add a comment starting with “globals” along with a comma-separated list of externally defined variables. For example, the following comment tells JSHint that “_” and “testdata” are defined by some other file:
/* globals _, uhdata */
Definition of variables and functions to be used elsewhere. Your file will often define variables and functions for “export” which are not used directly in your file. JSHint will complain that there is an unused variable. To fix this kind of error, add a comment starting with “exported” along with a comma-separated list of variables and functions intended for use elsewhere. For example, the following
/* exported testdata, maxDegrees */
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.