Static analyzers look at code and find problems before you run it. They do simple checks, like enforcing syntax (for example, tabs instead of spaces), and more holistic checks, like making sure your functions aren’t too complex. Static analyzers also find errors that you can’t find with testing, like instances of
== when you meant
In large projects and on big teams, you’ll be happy to have a little help finding those “simple” bugs that turn out to be a lot less simple than they looked.
JSLint, JSHint And Closure Compiler
var s = 'mystring';
for (var i = 0; i < s.length; i++)
JSLint will show two errors for this code:
Move 'var' declarations to the top of the function.
The first problem is the declaration of the variable
i at the top of the loop. JSLint also doesn’t like the
++ operator at the end of the loop declaration. It wants the code to look like this:
var s = 'mystring';
for (i = 0; i < s.length; i = i + 1)
I appreciate where JSLint is coming from, but it’s just too strict for me. It was too rigid for Anton Kovalyov6 as well, so he created JSHint.
JSHint works similarly to JSLint, but it’s written on top of Node.js7 and it’s much more flexible. JSHint has a long list of options8, making it possible to create custom checks by writing your own reporter9.
You can run JSHint from the website10, but most of the time you would install JSHint as a local command-line tool11 using Node.js. Once JSHint is installed, you can run it against your files with a command like this:
JSHint also has plugins for popular text editors, so you can run JSHint while you’re coding.
Closure Compiler, from Google, is a different breed. As the name suggests, it’s a compiler as well as a checker. It’s written in Java and based on the Rhino12 parser from Mozilla. Closure Compiler has a simple mode to do basic code checking, but it also has more advanced modes to do extra checking and enforce special type declarations.
Google makes a simple version of its compiler available on the Web13, but most of the time you’ll want to download Closure Compiler14 and run it locally.
Closure Compiler will output a list of files into a single minimized file after checking their code. You can run it like that after you’ve downloaded the
java -jar compiler.jar --js_output_file compress.js --js test1.js --js test2.js
Choosing the Right Checker
In my projects, I combine Closure Compiler with JSHint. Closure Compiler does the minimization and basic checking, while JSHint handles the more complex code analysis. The two work well together, and each covers some areas that the other doesn’t. In addition, I can use the extension capabilities of JSHint to write custom checkers. One common checker I write checks for particular functions that I don’t want, like calling functions that I don’t want to allow in my project.
Now that we’ve looked at a few checkers, let’s look at some bad code. All of these six examples are code you should never write and are spots where code checkers would keep you out of trouble.
This article uses JSHint for most examples, but Closure Compiler would produce similar warnings.
== Versus ===
===. Let’s look at an example.
var n = 123;
var s = '123';
if (n == s)
alert('The variables were equal');
if (n === s)
alert('The variables were identical');
== operator compares the values of the two objects. It converts the objects and compares them separately from their types. The
=== operator compares the object types and the values. In this case, the first
if block will pop up an alert, and the second
if block won’t — because
s have the same value but not the same type.
Check this code with JSHint and you’ll get this:
test.js: line 9, col 12, Expected '===' and instead saw '=='.
Undefined Variables And Late Definitions
Let’s start with some simple code:
var myVar = 'Hello, World';
See the bug? I make this mistake all the time. Run this code and you’ll get an error:
ReferenceError: myvar is not defined
Let’s make the problem a little more difficult to spot:
myVar = 'Hello, World';
Run this and you’ll get:
In the first case, JSHint will tell you this:
test.js: line 3, col 17, 'myvar' is not defined.
In the second case, it will tell you this:
test.js: line 2, col 5, 'myVar' is not defined.
test.js: line 3, col 17, 'myVar' is not defined.
The first case saves you from a runtime bug. You don’t have to test your app — JSHint will find the error for you. The second case is worse because testing won’t find the bug.
The problem with the second case is insidiously subtle and complex. The variable
myVar has now escaped from its function scope and been hoisted16 into the global scope for the whole page. This means that it will exist and have a value of
Hello, World after the
test function has run. This is called “global scope pollution.”
myVar variable will exist for every other function that runs after the
test function. Run the following code after you’ve run the
console.log('myVar: ' + myVar);
You’ll still get
Hello, World. The
myVar variable will hang around your code like mold, causing tricky bugs you won’t find until 3:00 am the night before you release, all because you forgot to type
var counter = 1;
counter.count = counter;
In this function we’re incrementing the
count property on the object that was passed in, but we need to add the property if it doesn’t already exist. See the bug?
This function will never add or increment a counter on anything. The
else statement will always be called, and it will redefine the function argument
counter. Basically this function creates a new object, assigns a property to it and then loses the object when the function returns. It will never change the object that was passed in.
This simple typo will make the code run without any errors but will produce a very strange result.
JSHint will tell you this:
test.js: line 21, col 21, 'counter' is already defined.
Curly Braces In Blocks, Loops And Conditionals
Will this code
doSomethingElse? At first glance, I always think it won’t
if statement merely as part of the block; the indenting doesn’t matter.
This issue is simply about code readability. If you can’t understand what the code will do, then you’ll write bugs.
Add the braces and you’ll always make code more readable. Skip them and JSHint will tell you this:
test.js: line 27, col 5, Expected '' and instead saw 'doSomething'.
Single And Double Quotes
console.log("This is a string. It's OK.");
console.log('This string is OK too.');
console.log("This string " + 'is legal, but' + "really not OK.");
Google has a code style guide that always uses single quotes for strings, so that they don’t have to escape double quotes in HTML. I can’t argue that single quotes are better than double quotes, but I can argue for consistency. Keeping everything consistent makes code more readable.
JSHint will warn you about mixed quotes like this:
test.js: line 31, col 27, Mixed double and single quotes.
Copying and pasting or mistyping a quote is easy. Once you have one bad quote, others will follow, especially if a lot of people are editing the file. Static analyzers will help keep the quotes consistent and prevent a big cleanup in the future.
Cyclomatic complexity17 is the measure of how complex a given block of code is. Look at the code and count the number of paths that could possibly run: That number is its cyclomatic complexity.
For example, this code has a cyclomatic complexity of 1:
return 'Hello, World!';
You can follow only one path through this code.
Let’s add a little conditional logic:
return 'Hello, World!';
return 'Hello, unWorld!';
The cyclomatic complexity has jumped to 2.
Ideal code is easy to read and understand. The higher the cyclomatic complexity, the more difficult the code will be to understand. Everyone agrees that high cyclomatic complexity is bad, but no one agrees on a limit; 5 is fine, and 100 is too high — but there’s a lot of gray area in the middle.
If the cyclomatic complexity gets to the predefined limit, then JSHint will let you know.
test.js: line 35, col 24, This function's cyclomatic complexity is too high. (17)
JSHint is the only one of the three checkers that looks at cyclomatic complexity. It also allows you to set the limit. Go above the
maxcomplexity number that you’ve set and JSHint will warn you. I like to set the limit to 14, but I’ll go a little higher in projects in which I do a lot of parsing or when I have other reasons to need many code paths.
The real reason the complexity number is important is that it tells you when to refactor your code. The first time you write a long function, it always makes sense. But if you wait six months and then come back to fix bugs, you’ll be glad that you took the time to make it easier to read.
Cyclomatic complexity usually breaks down with laundry lists. For example, I created a calendar, and I wanted to get the correct first day of the week for each country. I had a function that looked something like this:
if (country === 'USA')
else if (country === 'France')
I supported a lot of countries, so the cyclomatic complexity quickly grew to over 50. Although the code was very easy to read, the number of paths was high, so my code analyzer complained. In the end, I split up the function to get the complexity below my maximum. It was a hack for this particular case, but it’s a small price to pay for cleaner code overall.
Check Everything That You’ll Ever Edit More Than Once
Static checkers find the bugs that you wouldn’t come across with simple testing. They also find bugs at compile time, as opposed to runtime — those middle-of-the-night bugs that only creep in when a dozen people are all trying to do the same thing. Finding all of those subtle bugs is a long and painful process without code checking.
I began this article by claiming that I always use a code analyzer, but I don’t in one case: with throwaway code. I like using quick prototypes to show interactive ideas and to help my team come together on how something should work. Those prototypes are write-once code; I never need to fix bugs in them because I’ll be throwing away the prototypes a few weeks later. This throwaway code exists solely for the quick demos, and I don’t care if it has subtle bugs. Everything I care about, though, gets analyzed.
Fixing these types of bugs at the beginning of a project is easy; finding them on the night before you release will drive you nuts. Code analyzers have saved my butt many times, and they’ll also save yours.
Image on front page created by Ruiwen Chua18.
(al, ml, da)
- 1 http://www.jslint.com/
- 2 http://www.jshint.com/
- 3 https://developers.google.com/closure/compiler/
- 4 http://www.jslint.com/
- 5 https://code.google.com/p/jslint4java/
- 6 http://anton.kovalyov.net/
- 7 http://nodejs.org/
- 8 http://www.jshint.com/docs/options/
- 9 http://www.jshint.com/docs/reporters/
- 10 http://jshint.com/
- 11 http://jshint.com/install/
- 12 https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino
- 13 http://closure-compiler.appspot.com/home
- 14 https://developers.google.com/closure/compiler/
- 15 http://www.smashingmagazine.com/2013/04/18/introduction-to-programming-type-systems/
- 17 http://en.wikipedia.org/wiki/Cyclomatic_complexity
- 18 http://www.flickr.com/photos/7162499@N02/3260095534/
See the article here: