The last ten years have been interesting, to put it mildly. Ten years ago, some of us bought desktops, some of us bought laptops, and those of us with loads of cash bought both. Since then, you have a desktop for heavy-duty work, a laptop for portable stuff, and you may even have both a Mac and a PC. You also have a tablet for airplanes and cafes, a smartphone for everywhere else, and a smartwatch for even more everywhere else(!?). Some of us even have 17" screens in their new fancy cars. There are too many platforms - it's overwhelming. Not only is my wallet getting tired, this universal connectivity creates a real challenge for developers and for the people paying for those developers. I don't even want to begin thinking of the challenge it's creating for recruiters.

Ten years ago, if I wanted to build a product, I wrote a website or maybe a WinForms app. Today, I have to write code to cover every platform. Like I said, it's getting overwhelming. But there is this one language that runs everywhere: JavaScript. Open Web technologies, such as HTML and JavaScript, may have their shortcomings, but given their wide applicability, they are being used everywhere.

The second major megatrend powering this JavaScript revolution revolves around performance. I feel mildly amused when people brag about the huge amounts of storage they can now stuff into a small SD card. Yes, that's very impressive, but I wonder why people don't talk as much about how far JavaScript engines have come in the last few years. It wasn't that long ago that we were dealing with IE6. The debugging tools sucked and the performance was incredibly bad. Compared to the new Edge browser on the same hardware, IE6 gives you a million times slower performance. In other words, JavaScript engines have improved a thousand times more than your average storage technology.

The third megatrend is the widespread adoption of AJAX, and the amazing platforms that let us treat a Web page as a canvas for applications. Platforms such as AngularJS and React make it almost too easy to be free of the issues of postbacks, introducing design patterns such as MVC, and even converting to native code for mobile.

Naturally, this has had major implications. We're writing a LOT of JavaScript lately and this trend is not going to change anytime soon.

The Problem with JavaScript

As amazing as JavaScript is, the language itself plain sucks. It's amazing because it runs everywhere, but it sucks because it's so easy to write bad code in JavaScript, especially if you're coming from a C-like language such as Java, C++, or C#. In this article, I'll point out some of the most common mistakes that new-to-JavaScript developers make.

Why does JavaScript suck? The answer is simple: history. JavaScript was created by a really smart guy called Brendan Eich over a period of a week or so. He did it because Microsoft was largely perceived as evil, and his aim was to create a programming platform in the Netscape navigator. That way, the browser would run everywhere, and the OS wouldn't matter as much. HAHA! Down with Microsoft. You can see how well that worked out.

There was another company in Silicon Valley called Sun Microsystems, and they had their own plans to bring down the evil empire of Microsoft. They created a language called Java. Write once, run everywhere, or so they claimed. If you can run your code everywhere, why do you need a single OS? HAHA! Down with Microsoft. You can see how well that worked out for them, too.

The two Microsoft adversaries decided to shake hands. Then came the interesting challenge of having two tools solving the same problem. One was a browser scripting language and the other was Java. Clearly, this was a problem they could solve, not using technology but with clever marketing. Unsure of what to do, they decided to call this new scripting language “JavaScript,” the simple-minded younger brother of Java. The connotation was that you'd use Java for real work, and JavaScript for quick-and-dirty browser stuff. This unfortunate choice has been confusing recruiters and managers ever since.

Meanwhile, 1500 miles away in Redmond, Microsoft took note. They decided to get serious about both Java and JavaScript. For legal reasons, they couldn't just make Java and JavaScript available on their OS and browser. So they studied these languages very diligently and created Visual J++ and JScript, the Microsoft implementations of Java and JavaScript. Visual J++ had lots of Microsoft nuances thrown into it. But JScript was a very good copy of JavaScript. They copied all the mistakes and problems of the language too - with great accuracy. I wish they were as diligent in creating Windows ME, Vista, and 8. Wishes aside, they introduced an Internet Explorer-only language called VBScript that offered much richer functionality than JScript on the Windows platform. Even though IE and Windows had a footprint of 95% or more, open Web technologies eventually killed VBScript. The new language was called “Jscript.” As good a copy as it was of JavaScript, they couldn't legally call it JavaScript because Sun Microsystems was so litigious that they would sue a coffee shop with the word “Java” in its name. Now look who's evil.

As time went on, Sun Microsystems went on corporate welfare along with Microsoft, the guard changed, and the world in general decided that it would be good to standardize the language. So they submitted it to a standards body, agreed on the standards, but needed a good name for the language. They couldn't come up with a good name, so they called it “ECMAScript” (for now!), and maybe they would come up with a better name next week. That renaming never happened.

So now we're stuck with the name ECMAScript and even more confused recruiters (and managers).

The crux of this story is that the language, created with loads of imperfections, has grown, been standardized, and has been implemented all the way from your Pebble watch, to a Tesla screen, to Smartphones, browsers, desktops, and servers. This language, originally intended for 100-line quick-and-dirty scripts, is now being used to write enterprise applications with millions of lines of code.

Take all of that, add in the issues in the language, the lack of good tools, and the blight of overconfident developers, and you have way too much bad JavaScript floating around.

Writing good JavaScript requires you to understand three things:

  • The most common mistakes JavaScript developers make
  • Debugging techniques that will save you a lot of time and hair
  • ECMAScript 6 (ES6) and TypeScript, also known as the light at the end of the tunnel

Of course, there's a lot more to learn besides those three bullet points on this topic. In this article, I'll cover the most common mistakes that people make. In subsequent articles, I'll talk about how debugging helps and how ES6 and TypeScript will help.

Common JavaScript Mistakes

By far, the following isn't a comprehensive list of mistakes that smart developers can make. Developers can be rather creative, but here are a few of the most common mistakes.

Polluting the Global Namespace

This is pretty obvious: There is only one global namespace. If I create a variable called dummy, and you create a variable called dummy, the one who created it last wins. The best idea is: As much as possible, don't create global variables. The problem is that sometimes it's very easy to accidentally create global variables. For instance, check out the code below

(function(){
    var notglobal = 1;
    accidentalglobal = 1;
})();
global = 1;

In this code, the accidental global - even though it's inside a function scope - ends up being a global variable. This is because you didn't use the var keyword. The funny thing is, the above code will work, it's just prone to bugs. Let me explain why. Observe the following snippet for loop.

function doSomethingUseful() {
    for (i = 0; i < iteratable.length; i++) {
        iteratable[i];
    }
}

That may appear second nature to most C# programmers, but in JavaScript, it's truly awful. The problem is that the variable “i” is an accidentally global variable. The right way to write the code would be to say “var index = 0” instead of “i = 0”. And even var doesn't 100% protect you. Using var simply means that the variable is restricted to the function scope, but within the function, you may still be overwriting another variable called index. At least you'd be shooting your own foot then, not someone else's, but still, the best practice is that if you really wish to restrict a variable in its own scope, you should put it in its own function scope. That can get cumbersome, though, because you get way too many nested functions, so in reality, there's always a practical tradeoff between readability and maintainability. Luckily, ES6 has introduced a new keyword called let, which truly restricts your variable to the curly brackets scope.

Semicolon Carelessness

When writing code, always be clear and explicit about your intent. This applies to every language, and as time goes by, the language developers introduce interesting shorthand syntaxes that make code hard to read and easy to confuse. The irony is that they do so in the name of productivity. The few keystrokes they save you aren't worth the hair you will eventually lose figuring out what that code is actually doing. C#, I am looking at you.

Still, in JavaScript, using semicolons is optional. However, you should always end your statements with a semicolon. For instance, observe the code below:

function functionThatReturnsSomething() {
    return
    {firstName: "Sahil",lastName: "Malik"}
}

Although it may appear that the code is returning an object, it isn't. It is, in fact, returning a null, and then the free-hanging object either has no effect, or errors out, depending upon the browser. This is because the code got interpreted as "return;" instead of return object. Which brings me to my next point.

Curly Brace Fights

Statements such as if, while, for, etc., that require scopes denoted by curly braces can be written in three different ways. The first method is where you place the curly brace right after the statement, as shown below:

if (condition ) {
    statement;
}

The second, typically done by overconfident developers looking to show off, is where you omit the curly brace completely:

if (condition) statement;

And the third, usually done by developers who haven't ventured beyond Visual Studio much:

if (condition)
{
    statement;
}

The first method is the best method. It's error free, it's clear what your intent is, and it won't do accidental semicolon insertions under any condition.

Reserved Keywords

It's a good idea to keep an eye on all the reserved keywords both for the JavaScript you're writing today, and what the browser may upgrade to in the future. Using a future reserved keyword may mean that your application will break when the browser updates. I usually prefix variable names with a short prefix to keep my variable names separate from all reserved keywords.

Using typeof Blindly

The typeof keyword is supposed to return the type of the object, for instance, typeof 1 should return Number, and typeof "sahil" should return a string. Unfortunately, that's where the fun ends. Because beyond that, typeof returns all sorts of irregular results for objects. For instance, typeof null is an object, typeof [] should be an array, but its an object, and typeof NaN, which stands for “not a number,” is a number!

You can't rely on typeof to do type checking for you. You have to write polyfill methods to do this job. For instance, to really check whether or not an object is an array, you have to write a method, as shown in Listing 1.

Listing 1: The is_array method

var is_array = function (value) {
    return value &&
        typeof value === 'object' &&
        typeof value.length === 'number' &&
        typeof value.splice === 'function' &&
        !(value.propertyIsEnumerable('length'));
}

When parseInt isn't Very Reliable

One of the hardest things in JavaScript is that there is no static typing of variables. This means that tools can't help you much, and you tend to make more mistakes confusing data types. You declare a variable using the var keyword, which is optional. But when you've declared something, you can easily redefine it to anything you wish. Even if you don't redefine it accidentally, sometimes JavaScript internally changes a number to a string, or a 64bit number to a 32bit number etc.

Frequently, you have a variable that you think is a number, but in order to do math on it properly, you want to convert it into a strongly typed number. For instance, “1” (string) needs to be converted into 1 (Number). For situations like these, and more, use a method called parseInt. Unfortunately, parseInt is also not very reliable. What makes it worse is that the behavior can be inconsistent between browsers. For instance, consider this code below:

parseInt("07");

Some JavaScript engines interpret the above as 7 being converted with an octal radix, because you have a leading zero. The return value is 10. The leading zeros can be quite common when parsing dates for instance.

As a defensive coding practice, always use parseInt like this:

parseInt("07", 10);

The second parameter is the radix; always specify the radix explicitly.

But it doesn't end there. Consider these lines of code:

parseInt("12");
parseInt("12 monkeys");
parseInt("dozen monkeys");
parseInt("monkeys that are 12");

The first two lines return 12, and the last two return NaN, not a number. The parseInt looks at the first few number-like characters and does its best in returning what it could understand. This behavior is also different from most other languages.

Number Craziness

JavaScript has only one datatype for numbers, and it's called Number. It's a 64bit number most of the time. When you do a bitwise operation, the number internally converts to 32bit, and you could lose some fidelity. Also, the Number datatype is guaranteed safe for only 15 digits. To try this, run the next line of code (note that there are sixteen 9s).

console.log(9999999999999999);

You'd be shocked to see that the above line of code outputs 10000000000000000. It rounded it up for me. I didn't ask for it, but it did it anyway.

It's for these purposes that Number includes properties called MAX_SAFE_INTEGER and MIN_SAFE_INTEGER. If precision is important to you, you should stay within those boundaries.

But your headaches don't end there. Try the following in any browser's console:

0.1 + 0.2

Even a three-year-old could do the above math properly. JavaScript is almost 30 years old and can't do this basic math properly! JavaScript prints out the answer to the below as 0.30000000000000004. Technically speaking every language does this. In C#, if you add two floats, you'll get the same result. The big difference here is that languages such as C# and Java allow you to create numbers as decimals, making it very easy to do such calculations accurately. Those of us who have written financial programs know about the salami slice problem (see the sidebar) far too well. JavaScript requires you to do some interesting math, using the Math object to achieve the same results.

NaN in JavaScript stands for “not a number.” Unfortunately, typeof NaN returns “number.” How is “not a number” a number?

What's more interesting is if you compare NaN == NaN, the result is false. But if you set x = NaN, and check x !== x, it evaluates to true! These operators in JavaScript also cannot be trusted!

Operator Issues

JavaScript has many operators, but the two you need to be especially careful of are addition/subtraction operators and equality check operators.

For instance, consider the code:

var i = 1;
i = i + ""; // oops!
i + 1;  // evaluates to the String '11'
i - 1;  // evaluates to the Number 0

As you can see, the + operator is used to concatenate both strings and to add numbers. This would otherwise be fine, but when paired with a lack of static typing, data type deduction, or, should I say, data type guess work, this can be quite problematic. You have similar issues when using the ++ operator as well. For instance, consider the next bit of code:

var j = "1";
j++; // j becomes 2
var k = "1";
k += 1; // k becomes "11"

As can be seen, the ++, --, += and -= operators work in different ways. I prefer to avoid the ++ operator because it's confusing and doesn't add any value. Just keep your code clear by being explicit about what you mean. That way, there will be fewer errors, and it'll be easier to understand for the next unfortunate guy.

As if the + operator madness weren't enough, JavaScript plays interesting tricks when checking for equality. For instance, check out the code in Listing 2.

Listing 2: All these statements evaluate to true

0 == ""
0 == "0"
0 == " \t\r\n "
"0" == false
null == undefined

""    != "0"
false != "false"
false != undefined
false != null

All of these statements evaluate to true. How could that be possible? How is false not equal to "false"? The reality is that you should just avoid using == checks for equality. Instead you should use the typesafe checks using the === operator.

The with Statement

JavaScript has an interesting “with” statement that can lead to code behaving differently than you expect, depending upon the data at the time of execution. It almost goes without saying that you should avoid with. For instance, check out this code:

with (obj) {
    a = b;
}

It's impossible to tell what the code does by just looking at it. The code behaves differently if there is an obj.a property versus if there is a variable called “a”, and if those have any values in them.

Code that changes its execution based on data is by definition unreliable. Long story short: Avoid using with.

Arrays

JavaScript provides one way of storing data (besides objects) and that's arrays. Technically speaking, arrays are also objects, but let's leave that pedantic discussion for another day. The issue with arrays in JavaScript is that they're slow. They're slow and JavaScript has no alternate fast way of storing and retrieving data. Secondly, managing arrays is quite slow as well. You can delete an element in an array, but it leaves holes of undefined values in the array. “Packing up” the array and getting rid of those undefined values can be quite expensive.

I wish the problems ended there. Consider the next bit of code:

var arr = [1,2,3]
var total = 0;
for (var i in arr) {
    total = total + i;
}

Take a guess: What's the value of total at the end of the loop? Will it write “6”? No, it won't, because the “i” variable doesn't actually get the value of the array element. Instead, it gets a zero-based indexer. So will it print “3” (0+1+2)? Sadly not even that. It prints “0012”. Even though the indexer is supposed to be a number, the + operator interprets it as a string. In order to get the proper output, you have to add the following line in the for loop:

i = parseInt(i);

I was just checking to see if you were awake. That line has a bug in it too. What you really have to do is add the following line:

i = parseInt(i, 10);

This is because you must specify the radix or risk having your numbers interpreted as octal numbers. I explained this a little earlier in this article.

Now, I wish I could say that this is all that is wrong with JavaScript arrays, but wait until you try to sort them. For instance, consider this code:

[40, 100, 1, 5, 25].sort();

The output might surprise you! The output looks like this:

[1, 100, 25, 40, 5]

Upon close observation, you'll notice that the numbers are sorted as strings, even though the inputs were clearly numbers. If you really wanted to sort them as numbers, you have to pass in a comparer function like this:

[40, 100, 1, 5, 25].sort(
    function(a,b){return a-b;});

This is so confusing! Speaking of “this”…

This is So Confusing

JavaScript has a special keyword called this. The value of this changes depending upon the context; much like in the English language, “this” can be confusing. For instance, check out the code in Listing 3.

Listing 3: The confusing this keyword

var cars = {
    description: "Cars I like",
    carDetails: [{make:"Tesla", model:"Model S"}],
    tellMeAboutcars: function() {
        console.log(this);
        this.carDetails.forEach(function(car) {
            console.log(this);
            console.log(car.model + ":" + this.model);
        });
    }
}

cars.tellMeAboutcars();

When you run this code, it produces the output shown in Figure 1.

Figure 1: The output of Listing 3.
Figure 1: The output of Listing 3.

This is clearly not what you expected. Why is “this.model” undefined? It's because when you switched into the inner function's scope, the value of this changed to the context on which the function was being called, and that context, the value of this at that time, was the window object. It's therefore quite common practice to store the value of this as a private variable in the outer object or function, and reference the value of the object using the outer variable. Outer variables are accessible in inside scopes.

Scopes! How could I not mention scopes and closures in this article?

Understanding Scopes and Closures

Scopes are how you keep your code separate from other code. You want to keep your code separate because you don't want others to accidentally change your implementation, and vice versa. In most high-level languages, scope is defined using curly braces. For instance, observe the code shown in Listing 4.

Listing 4: Ineffective scopes using braces

{
    var outerScope = 1; {
        var innerScope = 2;
        document.writeln(innerScope);
        document.writeln(outerScope);
    }
        document.writeln(innerScope);
        document.writeln(outerScope);
}

The braces in the code shown have no meaning. They might as well not be there. In JavaScript, you don't have block scope. You have function scope. If you really wanted to have your own scope, you'd have instead written your code as shown in Listing 5.

Listing 5: The right way of doing scopes

var outerFunction = function(){
    var outerScope = 1;
    var innerFunction = function(){
        var innerScope = 2;
        document.writeln(innerScope);
        document.writeln(outerScope);
    };
    document.writeln(outerScope);
    innerFunction();
};
outerFunction();

By separating out your scopes into functions and actually calling the functions, you've now created separate scopes where you can “hide” values from others. And note that I'm calling the function; you could make a self-calling function, like this:

(function() {
    // write your logic here
})();

This sort of a self-calling function is called a closure. It gives you an area that's all for you. There's no danger of stepping over anyone else's variables or polluting the global namespace. This is how you should write JavaScript.

Function Hoisting

There are many other JavaScript WTFs to learn. I'll end with a really weird one called JavaScript hoisting.

Hoisting refers to the phenomenon where JavaScript takes the declaration of all variables and moves them to the top of your code automatically, irrespective of where they may appear in your code. The advantage, of course, is that you can write a function anywhere, and you can call it from anywhere. You don't have to worry about writing your functions in the right order. Do note, though, that initializations are not hoisted. The values are assigned where you left them; only the declarations are moved to the top.

Although that's a splendid idea, given how flexible JavaScript can be, some clever developers end up shooting themselves in the foot. For instance, let's say that I have ten buttons on the page and I want to create a function for handling the onclick event for each of those ten buttons.

What should I do to achieve this? Perhaps a for loop, and inside the for loop, I can write button.onClick = function() {..}? WRONG!

By doing this, you just hoisted ONE declaration of the function, not ten. Remember, JavaScript can't know ahead of time, during hoisting, how many times you might run the loop. As a result, you get ONE function, not TEN.

The proper way of handling this would be to move the creation of the function outside loop and return new instances of the function as many times as the loop runs.

In short, never put function declarations inside loops or if blocks! It will only lead to problems and more hair loss.

Summary

This article walked you through some common mistakes that the best of us make when working with JavaScript. Yes, JavaScript can suck! But don't forget, its dynamic nature is what also makes it so flexible. And its wide applicability is what makes it so productive and useful. One thing is clear: You need to master JavaScript. The intent of this article was not to scare you away from JavaScript, but point out some of the landmines that you could step on.

And thankfully, these problems are well recognized and a lot of smart people are working on sustainable solutions to these issues. The solutions may be in terms of frameworks, or they may be in terms of improvement to JavaScript itself, or perhaps creating transpilers and new languages such as TypeScript.

In subsequent articles, I'll tackle some of those improvements that make JavaScript a lot more fun.

Until then, happy hacking!