JavaScript Coding Guidelines EN (personal) - Gemorroj/noads-advanced GitHub Wiki

Javascript Style Guide

This is intended as a set of guidelines only. It is not intended to be a book of absolute rules and consistency with any existing code base should always be held as more important that this document.

It is also intended to be a living document and should be continuously updated where experience shows it to be incorrect.

TLDR;

  • FullCamel case for namespaces and constructors.
    
  • camelCase or mixed_underscore for variables and functions.
    
  • UPPERCASE_UNDERSCORED for constants.
    
  • Name verbosely, try to make function names explain what the function does.
    
  • 4 Spaces for indentation.
    
  • Spaces after colons and commas.
    
  • Use the constructor pattern.
    
  • Use named parameters where parameter meaning is not implicit in function calls.
    
  • Use 'self' for holding construction object in closure.
    
  • Use exceptions. Never code to fail silently.
    

Variable Declaration

Variables should be declared at the top of a function, even if the variable is not defined until later in the function. Variables that are assigned a value when they are declared should be on their own line; variables that are declared but that are not assigned a value may appear on the same line, but should appear at the end of the declaration statement. There should be a space before and after the =, and the variable names should be left-aligned with each other:

var foo = 1,
    bar = 2,
    yeah, this, way;

Variables should be named using camelCase. Use your judgment in creating variable names; for example, arrays should generally have a plural name, and it may make sense to give booleans a name that begins with is.

Indentation and Spacing

This project uses four spaces for indentation. Whether you like this or not, it is the way it is, and should be followed.

This code strips trailing whitespace from all lines. Commas and colons should always be followed by a space.

Strings

This code uses single quotes around strings.

Blocks

Because of implicit semicolon insertion, always start your curly braces on the same line as whatever they're opening. For example:

if (foo) {
    bar();
}

if (foo) {
    foo = 1;
    return false;
}

Commenting

Feel free to comment anything obscure, but there is no need to comment the obvious. Don't add meaningless, personal or vague comments.

If you find yourself needing to comment a lot, consider breaking the code up into named functions which serve to describe what the code is doing.

If working on a previously commented piece of code, be sure to change the comments to ensure they are still accurate.

Long comments should use /* ... */.

Single line comments should always be on their own line and be above the line they reference. Additionally there should be an extra endline above it. For example:

var some = "stuff";

// We're going to loop here
for (var i = 0; i < 10; i++ ) {
    doIt();
}

Inline comments are allowed as an exception when used to annotate special arguments in formal parameter lists:

function foo(types, selector, data, fn, /*INTERNAL*/ one ) {
    // do stuff.
}

Array and Object Initializers

Empty objects and arrays don't need filler spaces.

Single-line array and object initializers are allowed when they fit on a line.

var arr = [1, 2, 3];    // No space after [ or before ].
var obj = {a: 1, b: 2, c: 3};    // No space after { or before }.

Multiline array initializers and object initializers are indented just like a block.

var inset = {
    top: 10,
    right: 20,
    bottom: 15,
    left: 12
};

Note that values are aligned with no space between the key and the colon.

this.rows = [
    '"Slartibartfast" ',
    '"Marvin the Paranoid Android" ',
    '[email protected]'
];

Functions

Avoid using anonymous functions. Note that a function assigned to an object attribute is anonymous unless explicitly given a name:

bad:

this.do_the_magic = function () {
    //...
}

Although assigned to a meaningful attribute, this function itself is anonymous.

good:

this.do_the_magic = function do_the_magic() {
    //...
}

An application made up of entirely anonymous functions is almost impossible to effectively profile, and stack traces end up being pretty meaningless.

Some small functions passed as arguments (maybe as callbacks or event handlers) may not really have a suitable name, in which case leaving them anonymous is appropriate.

More about naming

Function naming is extremely important. Descriptive function names can really make the difference between readable and unreadable code. Be verbose. Having really long function or variable names is not a problem; JavaScript can be minified.

Calling Functions

Function calls should include spaces between arguments, but should not include spaces after the opening parenthesis or before the closing one:

foo('bar', 1);

Simple function calls should all be on one line:

activate(minigun, '10000+rounds');

Function calls with extremely long arguments or lots of them should be split into 1 line per argument, starting on the second line:

activate(
    the_super_multi_laser_array,
    'Piiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuu'
);

Additionally, if the arguments contain an object (hashlike), it should always be written multiline:

activate(
    the_super_multi_laser_array,
    settings: {
        voltage: Piiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuu,
        target:    "John Prescott's bald spot"
    }
);

Constructors

There are several different ways of constructing objects. In general the constructor pattern is recommended:

function Laser() {
    this.fire = function fire() {
        //...
    }
}

window.the_laser = new Laser();

Nested constructors are also fine but try to keep stuff separate where possible.

function Laser() {
    var power_supply = new PowerSupply();

    this.fire = function fire() {
        power_supply.turn_on();
        //...
    }

    function PowerSupply() {
        //...
    }
}

Self

Often, it's required to hold the constructed object in closure for use by private functions. Use the variable name self for this:

function Laser() {
    var self = this;

    function fire() {
        set_voltage(self.voltage);
    }
}

Note that this should always be the first line of the constructor.

Using named parameters

Using named parameters reduces connescence and makes code more readable. It is encouraged when a function's parameters are not immediately obvious from the function name.

Bad:

function do_the_thing(tempo, start_at_step, repeats) {
    //...
}

do_the_thing(120, 5, 3);

Here, it's not obvious reading the function call what the parameters are doing and anyone looking at the code would have to look up the function definition to understand the code.

Good:

function do_the_thing(params) {
        //params: tempo, start_at_step, repeats
    }

    do_the_thing({
        tempo:                 120,
        start_at_step: 5,
        repeats:             3
    });

or

function do_the_thing(params) {
    var tempo =         params.tempo;
    var start_at_step = params.start_at_step;
    var repeats =       params.repeats;
}

do_the_macarena({
    tempo: 120,
    start_at_step: 5,
    repeats: 3
});

Here, it's immediately apparent what the purpose of each parameter is when reading the function call.

Which parameters are consumed should be immediately visible at the start of the function definition. When defining a function, either var all the members of params which you are interested in using, or leave a comment saying which params are being used.

Modular Behaviour

Common behaviour can be bundled into modules:

function PoweredThing() {
    this.set_voltage = function set_voltage() {
        //...
    }
}

function Laser() {
    PoweredThing.apply(this);
}

var laser = new Laser();
laser.set_voltage(100000000);

Files

In general, one file should be created for each constructor or logical structure.

All files should be named in lower case and should reflect the constructor.

Code in views

It is acceptable to have inline javascript code, but only initializers related to the page content. In general, wherever possible, javascript code should be kept out of the view.

It's acceptable to set any required application constants inline.

Use of javascript in html attributes is bad:

<a onclick="do_something();">Do Something</a>

Make use of unobtrusive javascript in order to separate the functionality (the "behaviour layer") from a Web page's structure/content and presentation.

Language rules

Var

Always use var. Never use implicit assignment.

If you wish something to be declared on window, be explicit:

wrong:

foo = 'bar';

right:

window.foo = 'bar';

Constants

Constants should be defined as normal variables but should obey the naming conventions for constants. The const keyword is not well supported and thus should not be used.

Semicolons

Never rely on inferred insertion of semicolons. Semicolons are required after all assignments or function calls. Semicolons are not required after blocks.

It's also recommended to use semicolon after function declarations even t hough they are not required.

Semicolons should not be used after for / while / switch / if / else blocks and other similar constructions for readability.

var foo = 'bar';
// assignment - needs a semicolon.

function foo() {
    alert("don't you foo me");
    //function call, needs a semicolon.
};
// Doesn't need a semicolon, but should have one.

this.foo = function foo() {
    alert("don't you foo me");
};
// This must have a semicolon as it is an assignment.

if (foo) {
    foo();
}
// Should not have a semicolon.

Ternaries

The ternary operator can be very useful for variable assignment. However, nexted ternaries are not allowed. That is, the following construct should never be used:

var a = b ? c : d ? e : f;

Testing Equality

The strict equality operators (=== and !==) should always be used. If necessary, you should perform any variable coercion prior to the equality check.

Type Checks

Type checking should always be achieved via named methods: arr.isArray, str.isString, func.isFunction, and obj.isObject.

Nested Functions

Nested functions are a great way to break up a function and can be used liberally. If a function is used exclusively by another function, it should in general be declared within that function.

Bad:

function foo_plus_bar() {
    return get_foo_for_foo_plus_bar() + 'bar';
};
function get_foo_for_foo_plus_bar() {
    return 'foo';
};

Good:

function foo_plus_bar() {
    function get_foo() {
        return 'foo';
    };

    return get_foo() + 'bar';
};

As an exception to this, sometimes you may have to declare a 'child' function outside of the main function to avoid a circular reference (see section on closures).

Declarations within blocks

In general, avoid making any declarations within blocks. Especially, absolutely do not define functions within blocks as this is not part of ECMAScript. ECMAScript only allows for Function Declarations in the root statement list of a script or function.

Terrible:

if (foo_is_wibble) {
    function foo() { return 'wibble'; }
}
else {
    function foo() { return 'bar'; }
}
foo();

Ok:

function wibble() {
    return 'wibble';
}
function bar() {
    return 'bar';
}

var foo;
if (foo_is_wibble) {
    foo = wibble;
}
else {
    foo = bar;
}
foo();

In general, variables should also not be declared in blocks:

bad:

for (var i=0; i<5 i++) {
    var wibble = new Wibble(i);
    wibbles.push(bar);
}

good:

var wibble;
for (var i=0; i<5 i++) {
    wibble = new Wibble(i);
    wibbles.push(wibble);
}

Exceptions

Use exceptions. Throw stuff whenever you need to. The only thing to avoid is using try / catch blocks for 'happy path' execution. Try / catch blocks are expensive and should only be used for exception handling.

Don't be afraid of throwing custom exceptions. If something is broken, it's broken and a good error message will save time in debugger. Never code to fail silently.

Creating primitive objects

Do not use wrapper objects. Just declare the primitive directly:

var str = 'foo';
var bool = false;
var arr = [];
var num = 10;
var obj = {};

Especially, never use the new keyword with primitive constructors. Here's why:

typeof(Boolean()) // ==> 'boolean'
typeof(new Boolean()) // ==> 'object'

There are only five primitive types in javascript, these are undefined, null, boolean, number, and string.

Closures

Closures are great. Closures can inspire genuine awe. However, consideration must sometimes be taken in their use, especially in long-running code.

Consider this:

function foo(element, a, b) {
    element.onclick = function handle_click() { /* uses a and b */ };
}

Here, we have created a circular reference which will cause a memory leak. The function handle_click holds element in scope, and element holds handle_click, meaning that neither can be destroyed.

function foo(element, a, b) {
    element.onclick = bar(a, b);
}
function bar(a, b) {
    return function() { /* uses a and b */ }
}

Here a second closure not containing element is created, thus avoiding the circular reference.

Eval

In general, the only use for eval is to deserialize stuff. If you find yourself trying to use it for anything else, you're probably being too 'meta'.

With

In general, just don't. Use of the with keyword can easily mask scope and produce extremely confusing code to read.

This

Use of this can be confusing. In general it should be limited to being used within constructors and in conjunction with call or apply.

Using this to target DOM elements in event calls generally hints at a bad code structure, and consideration should be given to how to achieve the same results in a different way.

For … in … loops

This method should not be used to iterate over an array.

Modifying prototypes of built-in objects

for example:

Array.prototype.find = function () {....} 

Native wrapped constructors should never be modified. If you need to extend the functionality of a native type, create a new constructor which creates an object and then attaches any extended stuff to it.