Statements such as if and for can be used in two ways in JavaScript, with
curly braces for multiple contained lines or without curly
braces for one contained line. For example:
// Bad, though technically valid JavaScript
if(condition)
doSomething();
// Bad, though technically valid JavaScript
if(condition) doSomething();
// Good
if (condition) {
doSomething();
}
// Bad, though technically valid JavaScript
if (condition) { doSomething(); }The first two forms, which use an if statement without braces, are explicitly
disallowed in Crockford’s Code Conventions, the jQuery Core Style Guide, the SproutCore Style Guide, and the Dojo Style Guide. The omission of braces also generates warnings by default in
both JSLint and JSHint.
An overwhelming majority of JavaScript developers are in agreement that block statements should always use braces and always occupy multiple lines instead of one. This is because of the confusion created when braces aren’t included. Consider the following:
if (condition)
doSomething();
doSomethingElse();It’s difficult to tell the author’s intent in this code. There’s
clearly an error here, but it’s impossible to know whether the error is an
indentation error (the last line should not be indented) or braces are
missing because both line 2 and line 3 need to be executed inside the
if statement. Adding braces makes the
error easier to find. Here are two other examples with errors:
if (condition) {
doSomething();
}
doSomethingElse();
if (condition) {
doSomething();
doSomethingElse();
}In both of these examples, the code error is clear, as both obviously have indentation errors. The braces allow you to very quickly determine the author’s intent and make an appropriate change without fear of changing the code logic.
Braces should be used for all block statements, including:
if
for
while
do...while
try...catch...finally
A second topic related to block statements is the alignment of braces. There are two main styles of brace alignment. The first is to have the opening brace on the same line as the beginning of the block statement, as in this example:
if (condition) {
doSomething();
} else {
doSomethingElse();
}JavaScript inherited this style from Java, where it is documented in the Code Conventions for the Java Programming Language. This style also now appears in Crockford’s Code Conventions, the jQuery Core Style Guide, the SproutCore Style Guide, the Google JavaScript Style Guide, and the Dojo Style Guide.
The second style of brace alignment places the opening brace on the line following the beginning of the block statement, as in this example:
if (condition)
{
doSomething();
}
else
{
doSomethingElse();
}This style was made popular by C#, as Visual Studio enforces this alignment. There are no major JavaScript guides that recommend this style, and the Google JavaScript Style Guide explicitly forbids it due to fears of automatic semicolon insertion errors. My recommendation is to use the previous brace alignment format.
Spacing around the first line of a block statement is also a matter of preference. There are three primary styles for block statement spacing. The first is to have no spaces separating the statement name, the opening parenthesis, and the opening brace:
if(condition){
doSomething();
}This style is preferred by some programmers because it is more compact, though some complain that the compactness actually inhibits legibility. The Dojo Style Guide recommends this style.
The second style is to have a space separation before the opening parenthesis and after the closing parenthesis, such as:
if (condition) {
doSomething();
}Some programmers prefer this style because it makes the statement type and condition more legible. This is the style recommended by Crockford’s Code Conventions and the Google JavaScript Style Guide.
The third style adds spaces after the opening parenthesis and before the closing parenthesis, as in the following:
if ( condition ) {
doSomething();
}This is the style prescribed in the jQuery Core Style Guide, because it makes all aspects of the statement start quite clear and legible.
I prefer the second style as a nice compromise between the first and third styles.
Developers tend to have a love-hate relationship with the switch statement. There are varying ideas about
how to use switch statements and how to
format them. Some of this variance comes from the switch statement’s lineage, originating in C and
making its way through Java into JavaScript without the exact same
syntax.
Despite the similar syntax, JavaScript switch statements behave differently than in
other languages: any type of value may be used in a switch statement, and any expression can be used
as a valid case. Other languages
require the use of primitive values and constants, respectively.
Indentation of the switch
statement is a matter of debate among JavaScript developers. Many use
the Java style of formatting switch
statements, which looks like this:
switch(condition) {
case "first":
// code
break;
case "second":
// code
break;
case "third":
// code
break;
default:
// code
}The unique parts of this format are:
The format of switch statements
is rarely included in style guides when this style is used, primarily
because it is the format that many editors use automatically.
Although this is the format that I prefer, both Crockford’s Code Conventions and the Dojo Style Guide recommend a slightly different format:
switch(condition) {
case "first":
// code
break;
case "second":
// code
break;
case "third":
// code
break;
default:
// code
}The major difference between this and the previous format is that
the case keyword is aligned to the
same column as the switch keyword.
Note also that there are no blank lines in between any parts of the
statement. JSLint expects this indentation format for switch statements by default and will warn if
a case is not aligned with switch. This option may also be turned on and
off via the “Tolerate messy white space” option. JSLint does not warn if
additional blank lines are included.
As with other aspects of coding style, this choice is completely a matter of preference.
Another popular source of debate is whether falling through from one case to another is an acceptable practice.
Accidentally omitting a break at the
end of a case is a very common source
of bugs, so Douglas Crockford argues that every case should end with break, return, or throw, without exception. JSLint warns when
one case falls through into
another.
I agree with those who consider falling through to be an acceptable method of programming, as long as it is clearly indicated, such as:
switch(condition) {
// obvious fall through
case "first":
case "second":
// code
break;
case "third":
// code
/*falls through*/
default:
// code
}This switch statement has two
obvious fall-throughs. The first case
falls through into the second, which is considered an acceptable
practice (even by JSLint) because there are no statements to run for
just the first case and there are no
extra lines separating the two case
statements.
The second instance is with case
"third", which falls through into the default handler. This fall-through is marked
with a comment to indicate developer intent. In this code, it’s obvious
that the case is meant to fall through and isn’t a
mistake. JSHint typically warns when a case falls through unless you include this
comment, in which case the warning is turned off because you’ve signaled
that this isn’t an error.
Crockford’s Code Conventions disallows fall-throughs in switch statements altogether. The jQuery Core
Style Guide mentions that fall-throughs are used in their code, and
the Dojo Style Guide gives an example with a fall-through comment. My
recommendation is to allow fall-throughs as long as a comment is used to
indicate that the fall-through is intentional.
Another point of contention with regard to switch is
whether a default case is required.
Some believe that a default should
always be included even if the default action is to do nothing, as
in:
switch(condition) {
case "first":
// code
break;
case "second":
// code
break;
default:
// do nothing
}You’re likely to find open source JavaScript code following this
pattern, including default and just
leaving a comment that nothing should happen there. Although no style
guides are explicit about this, both Douglas Crockford’s Code Conventions for the JavaScript
Programming Language and the Dojo Style Guide include default as
part of their standard switch
statement format.
My preference is to omit default when there is no default action and
annotate it using a comment, as in this example:
switch(condition) {
case "first":
// code
break;
case "second":
// code
break;
// no default
}This way, the code author’s intent is clear that there should be no default action, and you save some bytes by not including extra unnecessary syntax.
The with statement changes how
the containing context interprets variables. It allows properties and
methods from a particular object to be accessed as if they were local variables and functions, omitting the object
identifier altogether. The intent of with was to lessen the amount of typing
developers need to do when using multiple object members in close
proximity. For example:
var book = {
title: "Maintainable JavaScript",
author: "Nicholas C. Zakas"
};
var message = "The book is ";
with (book) {
message += title;
message += " by " + author;
}In this code, the with statement
is used to augment identifier resolution within the curly braces by
allowing the properties of book to be
accessed as if they were variables. The problem is that it’s hard to tell
where title and author originated from. It’s not clear that
these are properties of book and that
message is a local variable. This
confusion actually extends far beyond developers, with JavaScript engines
and minifiers being forced to skip optimization of this section for fear
of guessing incorrectly.
The with statement is actually
disallowed in strict mode, causing a syntax error and indicating the
ECMAScript committee’s belief that with should no longer be used. Crockford’s
Code Conventions and the Google JavaScript Style
Guide disallow the use of with. I strongly recommend avoiding the with statement, as it prevents you from easily
applying strict mode to your code (a practice I recommend).
There are two types of for loops:
the traditional for loop
that JavaScript inherited from C and Java, as well as the for-in loop that iterates over properties for an object. These two loops, though similar,
have two very different uses. The traditional for loop is typically used to iterate over
members of an array, such as:
var values = [ 1, 2, 3, 4, 5, 6, 7 ],
i, len;
for (i=0, len=values.length; i < len; i++) {
process(values[i]);
}There are two ways to modify how the loop proceeds (aside from using
a return or throw statement). The first is to use the
break statement. Using break causes the loop to exit immediately and not continue running even
if the loop hasn’t finished all iterations. For example:
var values = [ 1, 2, 3, 4, 5, 6, 7 ],
i, len;
for (i=0, len=values.length; i < len; i++) {
if (i == 2) {
break; // no more iterations
}
process(values[i]);
}The body of this loop will execute two times and then exit before
executing process() the third time, even if the
values array has more than three
items.
The second way to modify how a loop proceeds is through the use of
continue. The continue statement exits the loop immediately; however, the loop will continue
with the next iteration. Here’s an example:
var values = [ 1, 2, 3, 4, 5, 6, 7 ],
i, len;
for (i=0, len=values.length; i < len; i++) {
if (i == 2) {
continue; // skip just this iteration
}
process(values[i]);
}The body of this loop executes two times, skips the third time, and picks up with the fourth iteration. The loop will then continue until its last iteration unless otherwise interfered with.
Crockford’s Code Conventions disallows the use of continue. His assertion is that code using
continue can better be written using
conditions. For instance, the previous example can be rewritten as:
var values = [ 1, 2, 3, 4, 5, 6, 7 ],
i, len;
for (i=0, len=values.length; i < len; i++) {
if (i != 2) {
process(values[i]);
}
}Crockford argues that this pattern is easier for developers to
understand and less error prone. The Dojo Style Guide states explicitly that continue, along with break, may be used. My recommendation is to
avoid continue whenever possible, but
there is no reason to completely forbid it. The readability of the code
should dictate its usage.
JSLint warns when continue is used.
JSHint does not warn when continue is
used.
The for-in loop is used to iterate over properties of an object. Instead of
defining a control condition, the loop systematically goes through each
named object property and returns the property name inside of a variable,
as in:
var prop;
for (prop in object) {
console.log("Property name is " + prop);
console.log("Property value is " + object[prop]);
}A problem with for-in is that it
returns not only instance properties of an object but also all properties it inherits through
the prototype. You may thus end up with unanticipated results when
iterating through properties on your own object. For this reason, it’s
best to filter the for-in loop to only
instance properties by using hasOwnProperty(). Here’s
an example:
var prop;
for (prop in object) {
if (object.hasOwnProperty(prop)) {
console.log("Property name is " + prop);
console.log("Property value is " + object[prop]);
}
}Crockford’s Code Conventions require the use of hasOwnProperty() for all for-in loops. Both JSLint and JSHint warn when a for-in loop is missing a call to hasOwnProperty() by default (both allow this
option to be turned off). My recommendation is to always use hasOwnProperty() for for-in loops unless you’re intentionally looking
up the prototype chain, in which case it should be indicated with a
comment, such as:
var prop;
for (prop in object) { // include prototype properties
console.log("Property name is " + prop);
console.log("Property value is " + object[prop]);
}Another area of focus with for-in
loops is their usage with objects. A common mistake is to use for-in to iterate over members of an array, as
in this example:
// Bad
var values = [ 1, 2, 3, 4, 5, 6, 7],
i;
for (i in values) {
process(items[i]);
}This practice is disallowed in Crockford’s Code Conventions as well as the Google JavaScript Style
Guide due to the potential errors it may cause. Remember, the
for-in is iterating over object keys on
both the instance and the prototype, so it’s not limited to the
numerically indexed properties of the array. The for-in loop should never be used in this
way.