Shorter isn’t always better. Sometimes writing more means that things are actually faster to read. Two examples: First, familiar things are easier to understand. That can mean that using familiar, slightly more verbose, constructs can be preferable. Second, humans read tokens, not characters. Therefore, redBalloon is easier to read than rdBlln.
Good code is like a textbook. Most code bases are filled with new ideas and concepts. That means that if you want to work with a code base, you need to learn those ideas and concepts. In contrast with textbooks, the added challenge with code is that people will not read it linearly. They will jump in anywhere and should be able to roughly understand what is going on. Three parts of a code base help:
Don’t be clever, don’t make me think. There is a lot of clever code out there that uses in-depth knowledge of the language to achieve impressive terseness. Such code is usually like a puzzle and difficult to figure out. One does encounter the opinion that if people don’t understand such code, maybe they should really learn JavaScript first. But that’s not what this is about. No matter how clever you are, entering other people’s mental universes is always challenging. So simple code is not “stupid code”, it’s code where most of the effort went into making everything easy to understand. Note that “other people” includes your past selves. I often find that clever thoughts I had in the past don’t make sense to my present self.
Avoid optimizing for speed or code size. Much cleverness is directed at these optimizations. However, you normally don’t need them. On one hand, JavaScript engines are becoming increasingly smart and automatically optimize the speed of code that follows established patterns. On the other hand, minification tools rewrite your code so that it is as small as possible. In both cases, tools are clever for you, so that you don’t have to be.
Sometimes you have no choice but to optimize the performance of your code. If you do, be sure to measure and optimize the right pieces. In browsers, the problems are often related to DOM and HTML and not the language proper.
if (x !== undefined && x !== null) ... // my choice if (x != null) ... // equivalent
var obj = new Object(); // no var obj = {}; // yes var arr = new Array(); // no var arr = []; // yes var arr = new Array('a', 'b', 'c'); // never ever [6] var arr = [ 'a', 'b', 'c' ]; // yes var regex = new RegExp('abc'); // avoid if possible var regex = /abc/; // yes
Conditional operator. Don’t nest the conditional operator.
// Don’t: return x === 0 ? 'red' : x === 1 ? 'green' : 'blue'; // Better: if (x === 0) { return 'red'; } else if (x === 1) { return 'green'; } else { return 'blue'; } // Best: switch (x) { case 0: return 'red'; case 1: return 'green'; default: return 'blue'; }Abbreviating if statements. Don’t abbreviate if statements via logical operators.
foo && bar(); // no if (foo) bar(); // yes foo || bar(); // no if (!foo) bar(); // yes
Increment operator. If possible, use the increment operator (++) and the decrement operator (---) as statements, don’t use them as expressions. In the latter case, they return a value and while there is a mnemonic (if the operand comes first, its value is returned before incrementing/decrementing it), you still need to think to figure out what is going on:
// Unsure: what is happening? return ++foo; // Easy to understand ++foo; return foo;Miscellanous. Various other kinds of cleverness:
if (x === void 0) x = 0; // not that important, any more, thanks to ES5 if (x === undefined) x = 0; // preferable return x >> 0; // no return Math.round(x); // yes
Parameter default values. Using the Or (||) operator to assign default values to parameters is a common practice:
function f(x) { x = x || 0; // ok if (x === undefined) x = 0; // ok if (!x) x = 0; // ok ... }Generic methods. If you use methods generically, you can abbreviate Object.prototype as {} and Array.prototype as []. I’m ambivalent about this one. It is a hack (you are accessing a prototype property via an instance). But it reduces clutter and I expect engines to eventually optimize this pattern.
Object.prototype.hasOwnProperty.call(obj, propName) // ok {}.hasOwnProperty.call(obj, propName) // ok Array.prototype.slice.call(arguments) // ok [].slice.call(arguments) // okECMAScript 5 – trailing commas. Trailing commas in object literals are legal in ECMAScript 5 [7]:
var obj = { first: 'Jane', last: 'Doe', // legal: trailing comma };ECMAScript 5 – reserved words. ECMAScript 5 also allows you to use reserved words (such as new) as property names:
> var obj = { new: 'abc' }; > obj.new 'abc'
var result = foo('a', 'b'); var arr = [ 1, 2, 3 ]; if (flag) { ... }For anonymous functions, I follow Crockford’s rule of having a space after the keyword function. The rationale is this is what a named function expression looks like if you remove the name.
function foo() { ... } // named function expression function () { ... } // anonymous function expression
Four spaces per indentation level. Most code I am seeing uses spaces for indentation, because tabs are displayed so differently between applications and operating systems. I prefer 4 spaces per level of indentation, because that makes the indentation more visible.
One variable declaration per line. I don’t declare multiple variables with a single declaration.
// no var foo = 3, bar = 2, baz; // yes var foo = 3; var bar = 2; var baz;Advantages [8]: deleting, inserting and rearranging lines is simpler and the lines are automatically indented correctly.
Keep variable declarations local. If your function isn’t too long (which it shouldn’t be, anyway) then you can afford to be less careful w.r.t. hoisting and pretend that var declarations are block-scoped. That is, you can declare a variable in the context in which it is used (inside a loop [8], inside a then-block or an else-block, etc.). This kind of local encapsulation makes a code fragment easier to understand in isolation. Is is also easier to remove the code fragment or to move it somewhere else.
Put expressions with operators in parentheses. This helps with reading, because it is easier to make out the scopes of the operators. Two examples:
return result ? result : theDefault; // no return (result ? result : theDefault); // yes return foo === bar; // no return (foo === bar); // yes
Avoid closures for private data. If you want an object’s private data to be completely safe, you have to use closures. Otherwise, you can use normal properties [10]. One common practice is to prefix the names of private properties with underscores. The problem with closures is that code becomes more complicated (unless you put all methods in the instance, which is unidiomatic and slow) and slower (accessing data in closures is currently slower than accessing properties).
Write parens if a constructor has no arguments. I find that such a constructor invocation looks cleaner with parentheses.
var foo = new Foo; // no var foo = new Foo(); // yesBe careful about operator precedence. Use parens so that two operators don’t compete with each other – the result is not always what you might expect:
> false && true || true true > false && (true || true) false > (false && true) || true trueinstanceof is especially tricky:
> ! {} instanceof Array false > (!{}) instanceof Array false > !({} instanceof Array) trueHowever, I find method calls after a constructor unproblematic:
new Foo().bar().baz(); // ok (new Foo()).bar().baz(); // not necessary
> +'123' // no 123 > Number('123') // yes 123 > ''+true // no 'true' > String(true) // yes 'true'
Avoid this as an implicit parameter. this should only refer to the receiver of the current method invocation, it should not be abused as an implicit parameter. Rationale: such functions are easier to call and you can later switch to ECMAScript 6’s arrow functions [11]. More abstractly, I like to keep object-oriented and functional mechanisms separate.
// Avoid: function handler() { this.logError(...); } // Prefer: function handler(context) { context.logError(...); }
Check for the existence of a property via in and hasOwnProperty. This is more self-explanatory and safer than comparing with undefined or checking for truthiness.
// All properties: if (obj.foo) // no if (obj.foo !== undefined) // no if ('foo' in obj) ... // yes // Own properties: if (obj.hasOwnProperty('foo')) ... // ok if (Object.prototype.hasOwnProperty.call(obj, 'foo')) ... // safer
Abbreviate Object.prototype with {} and Array.prototype with [].
Object.prototype.hasOwnProperty.call(obj, 'foo') {}.hasOwnProperty.call(obj, 'foo') Array.prototype.slice.call(arguments) [].slice.call(arguments)
Fail fast. If you can, it’s best to fail fast and to not fail silently. JavaScript is only so forgiving (division by zero etc.), because the first version of ECMAScript did not have exceptions. For example: don’t coerce values, throw an exception. However, you have to find a way to recover gracefully from failure when your code is in production.