Chapter 1. Basic Formatting
At the core of a style guide are basic formatting rules. These rules govern how the code is written at a high level. Similar to the ruled paper used in schools to teach writing, basic formatting rules guide developers toward writing code in a particular style. These rules often contain information about syntax that you may not have considered, but every piece is important in creating a coherent piece of code.
Indentation Levels
The first decision to be made about your JavaScript style guidelines (and indeed, about those of most languages) is how to handle indentation. This is one of those topics on which debates can last for hours; indentation is about as close to religion as software engineers get. However, it is quite important to establish indentation guidelines up front, lest developers fall into the classic problem of reindenting every file they open before starting to work. Consider a file that looks like this (indentation has been intentionally changed for demonstration purposes):
if (wl && wl.length) { for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); if (s.hasOwnProperty(p)) { if (merge && type == 'object') { Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }
Just looking at this code quickly is difficult. The indentation
isn’t uniform, so it appears that the else
applies to the if
statement on the first line. However, closer
inspection reveals that the else
actually applies to the if
statement on
line 5. The most likely culprit is a mixture of indentation styles from
several different developers. This is precisely why indentation guidelines exist.
Properly indented, this code becomes much easier to understand:
if (wl && wl.length) { for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); if (s.hasOwnProperty(p)) { if (merge && type == 'object') { Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }
Ensuring proper indentation is the first step—this particular piece of code has other maintainability issues discussed later in this chapter.
As with most style guidelines, there is no universal agreement on how to accomplish indentation in code. There are two schools of thought:
- Use tabs for indentation
Each indentation level is represented by a single tab character. So indents of one level are one tab character, second-level indentation is two tab characters, and so on. There are two main advantages to this approach. First, there is a one-to-one mapping between tab characters and indentation levels, making it logical. Second, text editors can be configured to display tabs as different sizes, so developers who like smaller indents can configure their editors that way, and those who like larger indents can work their way, too. The main disadvantage of tabs for indentation is that systems interpret them differently. You may find that opening the file in one editor or system looks quite different than in another, which can be frustrating for someone looking for consistency. These differences, some argue, result in each developer looking at the same code differently, and that isn’t how a team should operate.
- Use spaces for indentation
Each indentation level is made up of multiple space characters. Within this realm of thinking, there are three popular approaches: two spaces per indent, four spaces per indent, and eight spaces per indent. These approaches all can be traced back to style guidelines for various programming languages. In practice, many teams opt to go with a four-space indent as a compromise between those who want two spaces and those who want eight spaces. The main advantage of using spaces for indentation is that the files are treated exactly the same in all editors and all systems. Text editors can be configured to insert spaces when the Tab key is pressed. That means all developers have the same view of the code. The main disadvantage of using spaces for indentation is that it is easy for a single developer to create formatting issues by having a misconfigured text editor.
Though some may argue that one indentation approach or another is superior, it all boils down to a matter of preference within the team. For reference, here are some indentation guidelines from various style guides:
I recommend using four spaces per indentation level. Many text editors have this level as a default if you decide to make the Tab key insert spaces instead. I’ve found that two spaces don’t provide enough visual distinction for my eyes.
Note
Even though the choice of tabs or spaces is a preference, it is very important not to mix them. Doing so leads to horrible file layout and requires cleanup work, as in the very first example in this section.
Statement Termination
One of the interesting, and most confusing, aspects of JavaScript is that statements may be terminated either with a newline or with a semicolon. This breaks from the tradition of other C-like languages such as C++ and Java, which require semicolons. Both of the following examples are therefore valid JavaScript.
// Valid var name = "Nicholas"; function sayName() { alert(name); } // Valid but not recommended var name = "Nicholas" function sayName() { alert(name) }
The omission of semicolons works in JavaScript due to a mechanism known as automatic semicolon insertion (ASI). ASI looks for places in the code where a semicolon is appropriate and inserts one if not found. In many cases, ASI guesses correctly and there isn’t a problem. However, the rules of ASI are complex and difficult to remember, which is why I recommend using semicolons. Consider the following:
// Original Code function getData() { return { title: "Maintainable JavaScript", author: "Nicholas C. Zakas" } } // The way the parser sees it function getData() { return; { title: "Maintainable JavaScript", author: "Nicholas C. Zakas" }; }
In this example, the function getData()
is
intended to return an object containing some data. However, the newline
after return
causes a semicolon to be inserted, which
causes the function to return undefined
. The function
can be fixed by moving the opening brace on to the same line as
return
.
// Works correctly, even without semicolons function getData() { return { title: "Maintainable JavaScript", author: "Nicholas C. Zakas" } }
There are scenarios where ASI may be applied, and I’ve found limiting ASI to help reduce errors. The errors are typically caused by misunderstanding how ASI works and assuming that a semicolon will be inserted when it will not. I have found that many developers, especially inexperienced ones, have an easier time using semicolons than omitting them.
Semicolon usage is recommended by Douglas Crockford’s Code Conventions for the JavaScript Programming Language (hereafter referred to as “Crockford’s Code Conventions”), the jQuery Core Style Guide, the Google JavaScript Style Guide, and the Dojo Style Guide. Both JSLint and JSHint will warn by default when semicolons are missing.
Line Length
Closely related to the topic of indentation is line length. Developers find it hard to work on code in which the lines are long enough to require horizontal scrolling. Even with today’s large monitors, keeping line length reasonable greatly improves developer productivity. Code convention documents for many languages prescribe that lines of code should be no longer than 80 characters. This length comes from a time when text editors had a maximum of 80 columns in which to display text, so longer lines would either wrap in unexpected ways or disappear off the side of the editor. Today’s text editors are quite a bit more sophisticated than those of 20 years ago, yet 80-character lines are still quite popular. Here are some common line length recommendations:
Code Conventions for the Java Programming Language specifies a line length of 80 characters for source code and 70 characters for documentation.
The Android Code Style Guidelines for Contributors specifies a line length of 100 characters.
The Unofficial Ruby Usage Guide specifies a line length of 80 characters.
The Python Style Guidelines specifies a line length of 79 characters.
Line length is less frequently found in JavaScript style guidelines, but Crockford’s Code Conventions specifies a line length of 80 characters. I also prefer to keep line length at 80 characters.
Line Breaking
When a line reaches the maximum character length, it must be manually split into two lines. Line breaking is typically done after an operator, and the next line is indented two levels. For example (indents are four spaces):
// Good: Break after operator, following line indented two levels callAFunction(document, element, window, "some string value", true, 123, navigator); // Bad: Following line indented only one level callAFunction(document, element, window, "some string value", true, 123, navigator); // Bad: Break before operator callAFunction(document, element, window, "some string value", true, 123 , navigator);
In this example, the comma is an operator and so should come last on the preceding line. This placement is important because of ASI mechanism, which may close a statement at the end of a line in certain situations. By always ending with an operator, ASI won’t come into play and introduce possible errors.
The same line-breaking pattern should be used for statements as well:
if (isLeapYear && isFebruary && day == 29 && itsYourBirthday && noPlans) { waitAnotherFourYears(); }
Here, the control condition of the if
statement is split onto a second line after
the &&
operator. Note that the
body of the if
statement is still
indented only one level, allowing for easier reading.
There is one exception to this rule. When assigning a value to a variable, the wrapped line should appear immediately under the first part of the assignment. For example:
var result = something + anotherThing + yetAnotherThing + somethingElse + anotherSomethingElse;
This code aligns the variable anotherSomethingElse
with something
on the first line, ensuring
readability and providing context for the wrapped line.
Blank Lines
An often overlooked aspect of code style is the use of blank lines. In general, code should look like a series of paragraphs rather than one continuous blob of text. Blank lines should be used to separate related lines of code from unrelated lines of code. The example from the earlier section Indentation Levels is perfect for adding some extra blank lines to improve readability. Here’s the original:
if (wl && wl.length) { for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); if (s.hasOwnProperty(p)) { if (merge && type == 'object') { Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }
And here is the example rewritten with a few blank lines inserted:
if (wl && wl.length) { for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); if (s.hasOwnProperty(p)) { if (merge && type == 'object') { Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }
The guideline followed in this example is to add a blank line before
each flow control statement, such as if
and for
.
Doing so allows you to more easily read the statements. In general, it’s a
good idea to also add blank lines:
None of the major style guides provide specific advice about blank lines, though Crockford’s Code Conventions does suggest using them judiciously.
Naming
“There are only two hard problems in Computer Science: cache invalidation and naming things.” —Phil Karlton
Most of the code you write involves variables and functions, so determining naming conventions for those variables and functions is quite important to a comprehensive understanding of the code. JavaScript’s core, ECMAScript, is written using a convention called camel case. Camel-case names begin with a lowercase letter and each subsequent word begins with an uppercase letter. For example:
var thisIsMyName; var anotherVariable; var aVeryLongVariableName;
Generally speaking, you should always use a naming convention that follows the core language that you’re using, so camel case is the way most JavaScript developers name their variables and functions. The Google JavaScript Style Guide, the SproutCore Style Guide, and the Dojo Style Guide all specify use of camel case in most situations.
Even with the general naming convention of camel case in place, some more specific styles of naming are typically specified.
Note
Another notation called Hungarian notation was popular for
JavaScript around the year 2000. This notation involved prepending a
variable type identifier at the beginning of a name, such as sName
for a string and iCount
for an integer. This style has now
fallen out of favor and isn’t recommended by any of the major style
guides.
Variables and Functions
Variable names are always camel case and should begin with a noun. Beginning with a noun helps to differentiate variables from functions, which should begin with a verb. Here are some examples:
// Good var count = 10; var myName = "Nicholas"; var found = true; // Bad: Easily confused with functions var getCount = 10; var isFound = true; // Good function getName() { return myName; } // Bad: Easily confused with variable function theName() { return myName; }
The naming of variables is more art than science, but in general,
you should try to make the variable names as short as possible to get
the point across. Try to make the variable name indicate the data type
of its value. For example, the names count
, length
, and size
suggest the data type is a number, and
names such as name
, title
, and message
suggest the data type is a string.
Single-character variable names such as i
, j
, and
k
are typically reserved for use in
loops. Using names that suggest the data type makes your code easier to
understand by others as well as yourself.
Meaningless names should be avoided. Names such as foo
, bar
,
and temp
, despite being part of the
developer’s toolbox, don’t give any meaning to variables. There’s no way
for another developer to understand what the variable is being used for
without understanding all of the context.
For function and method names, the first word should always be a verb, and there are some common conventions used for that verb:
Verb | Meaning |
can | Function returns a boolean |
has | Function returns a boolean |
is | Function returns a boolean |
get | Function returns a nonboolean |
set | Function is used to save a value |
Following these conventions as a starting point makes code much more readable. Here are some examples:
if (isEnabled()) { setName("Nicholas"); } if (getName() === "Nicholas") { doSomething(); }
Although none of the popular style guides go to this level of detail regarding function names, these are pseudostandards among JavaScript developers and can be found in many popular libraries.
Note
jQuery quite obviously doesn’t follow this naming convention for
functions, partly due to how methods are used in jQuery, as many act
as both getters and setters. For example, $("body").attr("class")
returns the value of
the class
attribute, and $("body").attr("class", "selected")
sets the
value of the class
attribute.
Despite this, I still recommend using verbs for function names.
Constants
JavaScript had no formal concept of constants prior to ECMAScript 6. However, that didn’t stop developers from defining variables to be used as constants. To differentiate normal variables (those meant to have changing values) and constants (variables that are initialized to a value and never change), a common naming convention evolved. The convention comes from C and uses all uppercase letters with underscores separating words, as in:
var MAX_COUNT = 10; var URL = "http://www.nczonline.net/";
Keep in mind that these are just variables using a different naming convention, so it’s still possible to overwrite the values. Normal variables and constants are easily differentiated by using this very different convention. Consider the following example:
if (count < MAX_COUNT) { doSomething(); }
In this code, it’s easy to tell that count
is a variable that may change and
MAX_COUNT
is a variable that is
intended to never change. This convention adds another level of
semantics to the underlying code.
The Google JavaScript Style Guide, the SproutCore Style Guide, and the Dojo Style Guide specify that constants should be formatted in this manner (the Dojo Style Guide also allows constants to be specified as Pascal case; see the following section).
Constructors
JavaScript constructors are simply functions that are used to create
objects via the new
operator. The
language contains many built-in constructors, such as Object
and RegExp
, and developers can add their own
constructors to create new types. As with other naming conventions,
constructors follow the native language, so constructors are formatted
using Pascal case.
Pascal case is the same as camel case except that the initial letter is uppercase. So
instead of anotherName
, you would use
AnotherName
. Doing so helps to
differentiate constructors from both variables and nonconstructor
functions. Constructor names also are typically nouns, as they are used
to create instances of a type. Here are some examples:
// Good function Person(name) { this.name = name; } Person.prototype.sayName = function() { alert(this.name); }; var me = new Person("Nicholas");
Following this convention also makes it easier to spot errors
later. You know that functions whose names are nouns in Pascal case must be
preceded by the new
operator. Consider
the following:
var me = Person("Nicholas"); var you = getPerson("Michael");
Here, line 1 should jump out as a problem to you, but line 2 looks okay according to the conventions already laid out in this chapter.
Crockford’s Code Conventions, the Google JavaScript Style
Guide, and the Dojo Style Guide all recommend this practice. JSLint will warn if a constructor is found without an
initial uppercase letter or if a constructor function is used without
the new
operator. JSHint will warn if a constructor is found without an initial
uppercase letter only if you add the special newcap
option.
Literal Values
JavaScript has several types of primitive literal values: strings, numbers,
booleans, null
, and undefined
. There are also object literals and
array literals. Of these, only booleans are self-explanatory in their use.
All of the other types require a little bit of thought as to how they
should be used for optimum clarity.
Strings
Strings are unique in JavaScript, in that they can be indicated by either double quotes or single quotes. For example:
// Valid JavaScript var name = "Nicholas says, \"Hi.\""; // Also valid JavaScript var name = 'Nicholas says, "Hi"';
Unlike other languages such as Java and PHP, there is absolutely no functional difference between using double quotes and single quotes for strings. They behave exactly the same, except that the string delimiter must be escaped. So in this example, in the string using double quotes, we had to escape the double quote characters, and in the string using single quotes, we did not. What matters is that you pick a single style and stick with it throughout the code base.
Crockford’s Code Conventions and the jQuery Core Style Guide both specify the use of double quotes for strings. The Google JavaScript Style Guide specifies the use of single quotes for strings. I prefer using double quotes, because I tend to switch back and forth between writing Java and JavaScript frequently. Because Java uses only double quotes for strings, I find it easier to switch between contexts by maintaining that convention in JavaScript. This sort of issue should always be a consideration when developing conventions: do what makes it easiest for engineers to do their jobs.
Another aspect of strings is the hidden ability to create multiline strings. This feature was never specified as part of the JavaScript language but still works in all engines:
// Bad var longString = "Here's the story, of a man \ named Brady.";
Although this is technically invalid JavaScript syntax, it effectively creates a multiline string in code. This technique is generally frowned upon because it relies on a language quirk rather than a language feature, and it is explicitly forbidden in the Google JavaScript Style Guide. Instead of using multiline strings, split the string into multiple strings and concatenate them together:
// Good var longString = "Here's the story, of a man " + "named Brady.";
Numbers
The number type is unique to JavaScript, because all types of numbers—integers and floats—are stored in the same data type. There are also several literal formats for numbers to represent various numeric formats. Most formats are fine to use, but some are quite problematic:
// Integer var count = 10; // Decimal var price = 10.0; var price = 10.00; // Bad Decimal: Hanging decimal point var price = 10.; // Bad Decimal: Leading decimal point var price = .1; // Bad: Octal (base 8) is deprecated var num = 010; // Hexadecimal (base 16) var num = 0xA2; // E-notation var num = 1e23;
The first two problematic formats are the hanging decimal point, such as 10.
, and the leading decimal point, such as .1
. Each format has the same problem: it’s
hard to know if the omission of values before or after the decimal point
are intentional. It could very well be that the developer mistyped the
value. It’s a good idea to always include digits before and after the
decimal point to avoid any confusion. These two formats are explicitly
forbidden in the Dojo Style Guide. Both JSLint and JSHint warn when one of these two patterns is
found.
The last problematic numeric format is the octal format. JavaScript’s support of octal numbers has
long been a source of error and confusion. The literal number 010
doesn’t represent 10; it represents 8 in
octal. Most developers aren’t familiar with octal format, and there’s
rarely a reason to use it, so the best approach is to disallow octal
literals in code. Although not called out in any of the popular style
guides, both JSLint and JSHint will warn when they come across an octal
literal.
Null
The special value null
is
often misunderstood and confused with undefined
. This value should be used in just a
few cases:
To initialize a variable that may later be assigned an object value
To compare against an initialized variable that may or may not have an object value
To pass into a function where an object is expected
To return from a function where an object is expected
There are also some cases in which null
should not be used:
Do not use
null
to test whether an argument was supplied.Do not test an uninitialized variable for the value
null
.
Here are some examples:
// Good var person = null; // Good function getPerson() { if (condition) { return new Person("Nicholas"); } else { return null; } } // Good var person = getPerson(); if (person !== null) { doSomething(); } // Bad: Testing against uninitialized variable var person; if (person != null) { doSomething(); } // Bad: Testing to see whether an argument was passed function doSomething(arg1, arg2, arg3, arg4) { if (arg4 != null) { doSomethingElse(); } }
The best way to think about null
is as a placeholder for an object. These
rules are not covered by any major style guide but are important for
overall maintainability.
Note
A longer discussion around the pitfalls of null
is found in Chapter 8.
Undefined
The special value undefined
is
frequently confused with null
.
Part of the confusion is that null ==
undefined
is true. However, these two values have two very
different uses. Variables that are not initialized have an initial value
of undefined
, which essentially means
the variable is waiting to have a real value. For example:
// Bad var person; console.log(person === undefined); //true
Despite this working, I recommend avoiding the use of undefined
in code. This value is frequently
confused with the typeof
operator
returning the string “undefined” for a value. In fact, the
behavior is quite confusing, because typeof
will return the string “undefined” both
for variables whose value is undefined
and for undeclared variables.
Example:
// foo is not declared var person; console.log(typeof person); //"undefined" console.log(typeof foo); //"undefined"
In this example, both person
and foo
cause typeof
to return “undefined” even though they
behave very different in almost every other way (trying to use foo
in a statement will cause an error, but
using person
will not).
By avoiding the use of the special value undefined
, you effectively keep the meaning of
typeof
returning “undefined” to a
single case: when a variable hasn’t been declared. If you’re using a
variable that may or may not be assigned an object value later on,
initialize it to null
:
// Good var person = null; console.log(person === null); //true
Setting a variable to null
initially indicates your intent for that variable; it should eventually
contain an object. The typeof
operator
returns “object” for a null
value, so
it can be differentiated from undefined
.
Object Literals
Object literals are a popular way to create new objects with a specific set of
properties, as opposed to explicitly creating a new instance of Object
and then adding properties. For
example, this pattern is rarely used:
// Bad var book = new Object(); book.title = "Maintainable JavaScript"; book.author = "Nicholas C. Zakas";
Object literals allow you to specify all of the properties within two curly braces. Literals effectively perform the same tasks as their nonliteral counterparts, just with more compact syntax.
When defining object literals, it’s typical to include the opening brace on the first line, then each property-value pair on its own line, indented one level, then the closing brace on its own line. For example:
// Good var book = { title: "Maintainable JavaScript", author: "Nicholas C. Zakas" };
This is the format most commonly seen in open source JavaScript
code. Though it’s not commonly documented, the Google JavaScript Style
Guide does recommend this format. Crockford’s Code Conventions recommends using object
literals over the Object
constructor
but does not specify a particular format.
Array Literals
Array literals, as with object literals, are a more compact way of defining arrays
in JavaScript. Explicitly using the Array
constructor, as in this example, is
generally frowned upon:
// Bad var colors = new Array("red", "green", "blue"); var numbers = new Array(1, 2, 3, 4);
Instead of using the Array
constructor, you can use two square brackets and include the initial members of the
array:
// Good var colors = [ "red", "green", "blue" ]; var numbers = [ 1, 2, 3, 4 ];
This pattern is widely used and quite common in JavaScript. It is also recommended by the Google JavaScript Style Guide and Crockford’s Code Conventions.
Get Maintainable JavaScript now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.