Hello. My name is Steve Ayers. And I'm a JSLintaholic.
Before writing this post, I thought long and hard about the best metaphor to explain my love for JSLint. I searched my soul for what could possibly make me so addicted to this interesting little code tyrant.
My first thought (and a very authentic one at that) was that JSLint appeals to my innate proclivity to obssession and compulsion. I enjoy being meticulous about things that I create, especially when it comes to programming. Now here was everything I ever dreamed. A tool that analyzed my code down to the number of spaces I used at the beginning of a line! Oh, happy day.
OK, that's part of it, I guess. My next thought was that maybe I'm just a submissive sadist who likes to be dominated. Maybe I like being told what to do and that my code is dirty. In the end, however, I knew it was a multitude of reasons.
A caveat before we proceed, though: JSLint is not for the weak of heart. It doesn't tell you what you want to hear. In fact, it will tell you so many things you DON'T agree with, that you'll probably end up hating it. Everywhere you read about JSLint, you'll inevitably read the same thing right after: JSLint will hurt your feelings. JSLint is TOO PICKY. JSLint is just a suggestion, relax!! Put down the baseball bat and back away calmly.
But all of the above is what makes it great. You see, Javascript is the wild west. There is no compiler like Java's, no strict conventions, no friendly confines to guide you on your way. You have a blank canvas on which to draw. Some people would say that's what makes Javascript so appealing. There are no rules! It's Thunderdome! But for someone like me, no rules is very discomfiting. I like knowing there's a clear way to go about things. One day, I was driving in the car with my wife, asking her for directions. We came to a stop sign and I said, 'which way do I go?' and her response was, 'you can go left or right'. I almost had a nervous breakdown.
JSLint provides those rules. It adds structure to an otherwise nebulous world. But, the rules and boxes it uses to govern aren't really why I'm writing this article. You see, if you are serious about learning Javascript or if you want to become better at it, then JSLint can help. My advice is to turn it on full bore, tolerate nothing, not even spaces instead of tabs. Yes, each of your precious Javascript files are going to explode in an inferno of hellfire, but that's where the learning begins. With each complaint it has about your code, research the ones you don't agree with and the ones you don't understand. As you do, you'll find that you discover all kinds of things about the language you didn't know. Here's a few I discovered:
JSLint Message: Combine this with the previous 'var' statement
Javascript does not have block scope, so those coming from a Java-esque world are sure to declare their variables in a just-in-time sort of fashion. However, since block scope does not exist in Javascript, it doesn't make sense to have variable declarations littered throughout the code. Declaring them at all at the top not only makes your code more readable, but also helps protect against variable name clashes that could arise and be hard to debug.
To further hammer the point home, take a look at this basic for-loop construct. Typing this will get you flagged by the Lint Police:
<code>for (var count = 0; count < 5; count += 1) { console.log('Print' + count); } </code>
This particular complaint was the source of a heated discussion I had with a colleague. The above construct is perfectly legal and works completely fine as vanilla Javascript. However, it appears to create a block-level variable when it really doesn't. The variable 'count' is initialized at the very beginning of the function as undefined, so it will be available outside the scope of the loop and later on in the function. This again could cause hard-to-find bugs if you have some sort of variable name clash. For example, let's add a few lines of code:
<code>var count = 0; // Hundreds of lines of code // .... for (var count = 0; count < 5; count += 1) { console.log('Print' + count); } // Hundreds of lines of code // .... if (true) { count += 1; } console.log('Total count is: ' + count);</code>
Looking at the code could be confusing and lead one to believe that 'count' is actually two separate and separately-scoped variables, when in fact, it is not. The above code will print 6 as the final value of count, when the programmer may have intended it to print 1. So, one declaration at the top begins to make even more sense. It derives from an effort to eliminate confusion about the lack of block scope in the language.
JSLint Message: Move var declarations to the top
Javascript has a feature called 'hoisting' which means that when the code is run through the Javascript interpreter, all variables are 'hoisted' to the top of the function since remember, Javascript only has function scope. However, the tricky part about this is that any variable assignments are NOT hoisted. This can produce some really hard to find bugs.
For example, what does the following code alert?
<code>var myVar = true; function hoist () { if (myVar) { alert("Success"); } else { alert("Failure"); } var myVar = true; } hoist();</code>
If you run it, you'll see it actually prints 'Failure'. What gives?
This is because we actually have two 'myVar' variables here-- one in global scope, one in function scope. The programmer may have intended to declare a new function-level variable AFTER the If statement. What actually occurred was the interpreter hoisted the function-level myVar to the top of the function, but it didn't hoist the assignment. So, what was actually ran was this:
<code>var myVar = true; function hoist () { var myVar; if (myVar) { alert("Success"); } else { alert("Failure"); } myVar = true; } hoist();</code>
The '= true' assignment at the bottom of the function was not hoisted, only the declaration. After the hoist, no default value is present, so the function level 'myVar' was initialized to 'undefined'. And since 'undefined' is falsy, 'Failure' prints here. So, you can see that if you have any variable name clashes in your code between scopes, this can come back to bite you. Save yourself the trouble and just declare all variables at the top of your function.
JSLint Message: Expected ';' and instead saw...
OK, fine, we're getting a little picky here, but whatever. There is great reason for this. Many of you have probably noticed that writing Javascript can be forgiving at times. Forgot the ending semicolon, no problem! My code still works. That's because according to the ECMAScript spec, a semicolon can be automatically inserted when certain conditions are met. Wow! That's great! Its cleaning up its own messes! Java never did this for me. The compiler would smack my face if I tried something like this.
But its not all fun and games. You see there are instances when this is bad. Consider:
<code>return { name : "Steve" };</code>
Seems harmless, right? I simply used a variation on curly-brace placement. And there's no right or wrong answer for that, right? Heck, there is a WHOLE WIKIPEDIA PAGE about this:
But, in Javascript, things are a bit different. You see, in Javascript, the above becomes:
<code>return; { name : "Steve" };</code>
So, now instead of returning an object with Steve as it's name, it returns 'undefined' thanks to the Automatic Semicolon Insertion. So, that little complaint by JSLint actually can save a lot of headaches in the future.
JSLint Message: Expected === and instead saw ==
This was yet another source of debate between my colleague and I. JSLint is forcing you to use the === operator (and conversely !==) to test equality because its results are much more predictable than the slightly quirky == and !=.
The reasoning behind this is that the triple equals operator tests for equality without performing type coercion. Since Javascript is a dynamically typed language, the == operator will first try to coerce the values into the same type before testing equality. For === to return true, the objects must have the same value and ALREADY have the same type. This may seem benign at first, but it can lead to strange transitivity in your code. For example:
<code>var obj = {}; obj.firstProp; console.log(obj.firstProp == null); console.log(obj.firstProp == undefined);</code>
The above code will print true for both cases, even though null and undefined are not equal to each other. One would expect that firstProp here would be undefined by definition, but not null. Changing the code prints it as you would expect:
<code>var obj = {}; obj.firstProp; console.log(obj.firstProp === null); console.log(obj.firstProp === undefined);</code>
> false
> true
Some additional nutty examples, provided in Javascript: The Good Parts are:
'' == '0' // false
0 == '' // true
0 == '0' // true
So, you can see, while most of the time you may be safe from the quirks of the double-equals operators, the day will come when you're not. And good luck trying to find the error.
It's important to remember that in addition to protecting you from yourself and code with potential bugs, JSLint is also seeking to define a standard and a structure. So, yes, some of the things it complains about are ridiculous: spaces between commas, the use of += instead of ++ for an increment operator., etc. But, again remember, some of the guidelines are merely suggestions and ALL are able to be turned off or ignored through the use of directives you can put in your code.
But something to consider is that these suggestions were written by Douglas Crockford and trust me, Crockford knows his stuff. This isn't some fly-by-night approach to code quality someone found on a bathroom wall or marketed by Fat Sal's Cheese Steaks and Javascript Standards. This is DOUGLAS CROCKFORD. The guy invented JSON! So, let's just assume he probably knows what he's doing and that the styles and standards he puts forth are more apt to be correct than yours. You don't have to always agree, but at least try to understand where he's coming from and you'll realize your Javascript skills become that much stronger.
So yes, your code is filthy. Filthy and dirty. Clean it up, for God's sake. There's kids around.