Frontend Style Guide by Kaliop

JavaScript Guidelines

JavaScript syntax

  • Soft tabs, 4 spaces
  • Semicolon at the end of statement
  • Use camelCase for variables and functions,
  • UPPERCASE_WITH_UNDERSCORES for constants
  • (and TitleCase for constructor functions or ES6 classes)
var MAX_ITEMS = 10;
var data = getInitialData();

function updateData(element, options) {
    …
}

White space

  • Use 1 blank line when you want breathing room inside a module or complex function
  • Keep { on the current line, with one space before
  • Indent the body of functions and the contents of blocks
  • Do not use arbitrary numbers of spaces to align code
  • Use spaces before and after operators (=, +, % etc.)
// Bad
function doSomething()
{
    var DELAY=60*60*24;
    var MAGIC_WORDS=['abracadabra',
                     'hocus pocus'];
    …
}

// Better
function doSomething() {
    var DELAY = 60 * 60 * 24;
    var MAGIC_WORDS = [
        'abracadabra',
        'hocus pocus'
    ];
    …
}

Variable values

  • One var keyword for each variable (avoid var x, y)
  • Literal syntax for object and array creation
  • Prefer single quotes for strings
  • Use the + operator for multiline strings
  • Don’t use reserved words (class, default, and more) as variable or property names
// Good
var myArray = [];
var defaults = {
    className: 'MyComponent'
}
var multiLine = 'Through the coppice and ' +
    'the chaparral, the thickets thick with ' +
    'mold, the bracken and the brier… ' +
    'catchweed into the fold.';

// Baaaaaaad
var myArray = new Array(),
    default = new Object();
default.class = "MyComponent";
var multiLine = "Through the coppice and \
the chaparral, the thickets thick with \
mold, the bracken and the brier… \
catchweed into the fold.";

Always use var

  • Always use var to declare a variable.

Variables declared with the var keyword are scoped to the function where they are declared.

Variables declared without any keyword are added to the global object (window) in web browsers. This can create bugs in your scripts, or collisions with other scripts!

function doSomething() {
    var test1 = 'Good.';
    test2 = 'OH NO!'; // test2 is global!
    console.log(window.test1); // undefined
    console.log(window.test2); // 'OH NO!'
}
// Variables in a block (for loop, if/else, etc.)
// live in the parent scope
for (var i=0; i<10; i++) {
    var test3 = 'Can you see me?';
}
console.log(i); // 10
console.log(test3); // 'Can you see me?'

Comments

  • Use single-line comments (// …) to comment on the next line or the next few lines of code.
  • Use a JSDoc-style comment (/** … */) before a complex function or a module.
/**
 *  Make a header "sticky"
 *  (Adds/removes a class when scrolling)
 */
function stickifyHeader(element, activeClass, offset) {

    // We had a latency of 250ms but we had too many
    // times when quick scrolling didn't update the
    // header's state.
    var LATENCY = 100;

    …
}

Functions

  • Create functions to organize your code
  • Prefer function declarations to anonymous function expressions
  • It’s okay and often useful to declare functions inside a function

A note on scope

A JavaScript function declared inside a function will live in the parent function’s scope (unlike in PHP). It will also have access to all variables and functions defined in the parent function.

function foo() {
    var message = 'It works.';
    function bar() {
        return 'Awesome! ' + message;
    }
    return bar();
}
bar(); // ReferenceError: bar is not defined
foo(); // 'Awesome! It works.'
// Bad style
var myFunction = function() { … }

// Better
function myFunction() { … }
/**
 *  Using functions to organize code.
 *  such code, much organized, wow
 */
function initUIModule() {
    var KEYDOWN_DELAY = 100;

    function startUp() {
        …
    }
    function toggle(event) {
        …
    }
    function manageKeyEvent(event) {
        …
    }

    // Init stuff
    startUp();
    element1.on('click', toggle);
    element2.on('keydown', manageKeyEvent);
}

Use a module pattern

Module patterns in JavaScript can be used for:

  1. organizing your code;
  2. making sure that the variables and functions for a given group of code are not declared in the global scope (i.e on the host object, window).

The IIFE pattern (for immediately invoked function expression) can be used to create an anonymous function scope, so that your variables do not leak into the global namespace. There are different possible syntax. Prefer the one in the next example.

/**
 * IIFE pattern
 */
;(function(){
    // Your code goes here
})();
/**
 * IIFE pattern with global variables passed to
 * the function. In practice, this means that we
 * take `window.jQuery`, and use it locally as `$`.
 */
;(function($){
    // Your code goes here
})(jQuery);

Prefer strict equality

  • Prefer the strict equals operator (===)
  • Avoid the equals operator (==)
  • Be careful when using if (x), if (!x), && or ||

Why prefer strict equals?

  • x === y means “are x and y exactly the same?”
  • x == y means “compare x to y… are they equal after perhaps converting one or both of them to a different type according to very specific rules which few JavaScript developers know by heart?”

Using == works alright when both terms of the comparison have the same type (two strings, two numbers, etc.). But if the types are different, JavaScript will do some funky conversions that can be hard to predict if you don’t know all the rules.

Same thing for difference

Use x !== y, and avoid x != y.

Conversion to boolean

JavaScript will convert a value to a boolean type (same as using Boolean(x)) when you use if (x), if (!x), x && …, x || …, or x ? …. Here’s how conversion to boolean works:

  • Only a few values will convert to false: the number 0, the empty string '', false, undefined and null.
  • Everything else will convert to true. This includes negative numbers, empty objects and empty arrays.
// Working with an element like this:
// <div aria-hidden="false"></div>
var isHidden = div.getAttribute('aria-hidden');

console.log(isHidden); // 'false'
console.log(Boolean(isHidden)); // true

// Now, guess which is better?
if (isHidden) { … }
if (isHidden === 'true') { … }
// Broken check for default values!
function doSomething(limit, recursive) {
    limit = limit || 10; // Can’t do limit=0
    recursive = recursive || true; // BROKEN
    …
}
// Fixed!
function doSomething(limit, recursive) {
    if (typeof limit !== 'number') { limit = 10 }
    if (typeof recursive !== 'boolean') { recursive = true }
    …
}

Avoid the `this` keyword if possible

The this keyword can be different things, depending on context. Knowing what this refers to in different contexts is not so hard, but it can confuse beginners.

  • Don’t use this if you can use something else.
  • Use event.target and event.currentTarget in event handlers.
  • If you need to use this and are not sure what it refers to in the context where you’re writing your code, use console.log(this) to get a better idea. (Or add a breakpoint in the JS debugger.)

jQuery

Some jQuery methods, such as $(x).each(), change the value of this in the callback function to the current HTML element in the loop.

$('p').each(function() {
    // this is a DOM element (a paragraph)
    this.setAttribute('title', 'Hello!');
    // And $(this) creates a jQuery object
    // wrapping the paragraph
    $(this).attr('title', 'Hello!');
});

You could rely on this, but if you want your code to be more explicit (and more JavaScript-like, rather than jQuery-like), use named arguments and don’t use this.

$('p').each(function(index, element) {
    $(element).attr('title', 'Hello!');
});
// Event handler: use `event.currentTarget`
$('button').on('click', function(event) {
    console.log(event.currentTarget === this); // true
    // You could write:
    $(this).addClass('clicked');
    // But please be explicit instead:
    $(event.currentTarget).addClass('clicked');
});
// Use explicit arguments with jQuery.each (and other methods)
someElements.each(function(index, element) {
    // Let’s look at what we’re working with
    console.log(index, element);
    // Do something now with element or $(element)
    …
});

Beyond console.log()

  • Make sure to remove all console.log() calls from your code before committing.
  • Browsers have more advanced JS debugging tools for inspecting and debugging JS code (e.g. “Sources” tab in Chrome Devtools, “Debugger” in Firefox Devtools). They’re really cool, so you should learn to use them.