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 (avoidvar 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:
- organizing your code;
- 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 “arex
andy
exactly the same?”x == y
means “comparex
toy
… 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 number0
, the empty string''
,false
,undefined
andnull
. - 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
andevent.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, useconsole.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.