Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

Top 10 Things that JavaScript Got Wrong

by

JavaScript, if only by default, is one of the most popular programming languages available. Over the years, it's been labeled as a nightmare to work with, and, to some extent, this is true! However, more often than not, what people mean to say is that the DOM API is a nightmare. Nevertheless, there are a handful of flat-out errors in the language.

I'd like to make a note that I love JavaScript. This article is only meant for some fun, and for us to be aware of some its short-comings.

1. The Name. JavaScript is NOT Java

We'll start with a fun jab at the name choice. While it was originally called Mocha, and then LiveScript, it was later changed to JavaScript. According to history, its similarities to the name Java was the result of a collaboration between Netscape and Sun, in exchange for Netscape bundling the Java runtime within their popular browser. It's also been noted that the name came, almost as a joke, due to the rivalry between LiveScript and Java for client-side scripting.

Nevertheless, it resulted in thousands of "JavaScript has nothing to do with Java" comments in forums across the web!

2. Null is an Object?

Consider this...

console.log(typeof null); // object

This makes zero sense. If null is the absence of a value, then how could its type be "object?" The simple answer is that it's flat-out an error that dates back to the first release of JavaScript - one that was even incorrectly carried over to Microsoft's JScript.

3. NaN !== NaN

NaN, as we'd expect refers to a value that is not a legal number. The problem is that NaN isn't equal to anything...including itself.

console.log(NaN === NaN); // false

This should be wrong. Instead, if you want to determine if a value is indeed NaN, you can use the isNaN() function.

Update: after reading through some of the brilliant comments, particularly the ones relating to NaN being similar to infinity, it then makes perfect sense that NaN would not equal itself. But it can still be confusing. Refer to the comments for an in depth discussion on this!

4. Global Variables

The dependence upon global variables is widely considered to be far and away the worst part of JavaScript. For simple projects, much like the quick tips on this site, it doesn't truly make a difference. However, the real burden of globals come into play when you begin referencing multiple scripts, without any knowledge of how they're created, or named. If they happen to share the same name as one of your variables, your program is going to throw some sort of error.

"The problem with JavaScript isn't just that it allows them (global variables), it requires them." - Crockford

5. User-Agent Strings Report Mozilla. Ever Wonder Why?

Alright - this one isn't the fault of JavaScript. I cheated a bit. It's because of the browser vendors. Having said that, user-agent string detection is very common in JavaScript; so it's important to know what you're dealing with. It probably doesn't belong in this list, but who cares! It's good to know.

This one isn't as much a mistake as it was an unavoidable decision. For example, open Safari, access the Web Inspector, and log the user agent string into the console.

console.log(navigator.userAgent);
// Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10

Note that first string of characters: Mozilla/5.0. Why would Safari identify it as a Mozilla based browser? Though it later correctly identifies itself, that still doesn't explain why they'd bother to mislead programmers. In fact, you'll find that most browsers identify themselves as Mozilla. The answer goes back a decade, and is, again, less an error, and more an unavoidable circumstance.

For those unfamiliar, a user-agent string is simply meant to identify the browser and its version. As an example, the first ever browser, Mosaic, had a user-agent string that looked like so:

Mosaic/0.9     // browser name / version number

This makes perfect sense. And when Netscape came onto the scene, they kept Mosaic's usage, and also added an encryption type section.

Mozilla/2.02 [en] (Win95; I)     // browser name / version / encryption

So far so good. The problems came into play when - wait for it - Internet Explorer 3 was released. Keep in mind that, when they launched, Netscape was the most popular browser available. In fact, many servers and programs were already implementing user-agent detection in order to identify Netscape. Though this is a highly debated topic today, back then, it wasn't much of an issue. If IE had used their own user-agent string, it would have looked something like this:

MSIE/3.0 (Win95; U)

This would have left them at a huge disadvantage, because Netscape was already being identified by many servers. As such, the developers decided to incorrectly identify the browser as Mozilla, and then append an additional set of information labeling it as Internet Explorer.

Mozilla/2.0 (compatible; MSIE 3.0; Windows 95)

Nowadays, user-agent detection is a last effort, and its considered so precisely for this reason. You'll find that most browsers followed IE's lead in identifying themselves as Mozilla. Think of it as a chain reaction.

Further Reading

I highly recommend that you read Nicholas Zakas's "History of the User-Agent String," if you'd like to delve deeper.

6. Scope Inconsistencies

Consider the following code:

// Create a function that will call a function with the name equal to parameter fn.
function foo(fn) {
    if (typeof fn === "function") {
        fn();
    }
}

// Create an object with a property and a method. 
var bar = {
    barbar : "Hello, World!",
    method  : function() {
        alert(this.barbar);
    }
};

bar.method(); // Alerts Hello, World!
foo(bar.method); // If we call the foo function add pass the "bar.method" method, it somehow alerts "undefined."
foo(function() { bar.method(); }); // alerts Hello, World, after

The reason why foo(bar.method) does not render the same result is because the method function will be called as a method of the window object, rather than bar. To fix this, we must call bar.method() from within the passed anonymous function.

Thanks so much to Jeremy McPeak for notifying me of this error.

7. The Use of Bitwise Operators

JavaScript shares many similarities with Java - one of them being the set of bitwise operators.

  • & - and
  • | - or
  • ^ - xor
  • ~ - not
  • >> - signed right shift
  • ??? - unsigned right shift
  • << - left shift

Consider the first item, &; it would be much more efficient to use the && operator, as it's quicker. This is because JavaScript isn't the same as Java, and doesn't have integers. As such, a relatively length process is required to convert the operand, do something with it, and then convert it back.

This is why you can get away with using & for "and", and | for "or" - even though you should be using && and ||.

8. Too Many Falsy/Bottom Values

Maybe this isn't specifically an error in JavaScript, but it certainly makes the learning process, especially for beginners, a tough one. Values like null, false, and undefined almost mean the same thing, but there are differences that can be confusing to understand.

Falsy Values

To test, open up the console in Firefox, and find the boolean of the following items.

!!(0); // false
!!(false); // false
!!(''); // false
!!(null); // false
!!(undefined); // false
!!(NaN); // false

Please note that any other values will be interpreted as truthy.

More than an error, this many falsy values is just confusing!

9. It Can't Do Arithmetic

Okay, okay - I'm 99% teasing with the heading above. But JavaScript does have a few minor issues when working with decimals, for example, things like money transactions. For example, open up your console, and log ".2 + .4". We would expect it to display ".6", correct? Well it does, and it doesn't!

Math
console.log(.2 + .4); // 0.6000000000000001

How come? At a high level, it's because JavaScript used the IEEE Standard for Binary Floating-Point Arithmetic. I, probably like you, don't fully understand exactly what that specifies, but just know that, when dealing with decimal fractions, results can vary slightly from what you might expect. Keep in mind that integer arithmetic is perfect, so this really isn't a huge issue.

10. Code Styling Isn't your Choice!

When it comes to your coding style, it's exactly that: your style. Some people prefer to place their curly braces on the same line as the control, others prefer that it goes on its own.


 // braces on the right
return {
  foo : bar
};

// braces on their own line
return 
{
  foo : bar
};

Dependent upon the first web dev book we read, or how our teacher taught us, it's perfectly acceptable to use either of the methods above, or even a combination of the two. The problem with JavaScript is that it's not your choice!

I learned this particular example from a lecture that Doug Crockford gave around a year ago. Consider the return statement from above. Believe it or not, they ARE NOT equal. Don't believe me? Try this out. Add the following to some HTML page.

var foo = function() {
	
	return {
		a : 'b'
	};
	
}();

alert(foo.a); // b

The code above simply creates a variable called foo, which is equal to the returned object. When we alert(foo.a), we, as expected, see an alert box with a value of 'b.' Now, simply take that opening curly brace, from the return statement, and push it down to its own line, like so.

return
{
	a : 'b'
};

If you run it in your browser again, you'll receive a Firebug error, logging that "foo is undefined." What the hell!? :)

So why does JavaScript do this? It's because of something called "semicolon insertion." Essentially, JavaScript will attempt to correct our bad coding. If, for instance, it thinks that you've left off a closing semicolon, it'll go ahead and add it on for you. Though this was originally intended to be a convenience, especially for newer JavaScripters, it's actually a very bad thing when you don't have control over your own code, as demonstrated above.

In our example, there's no way to determine why foo.a returns "undefined. " Now that we're aware of semicolon insertion, the reason it's undefined is because JavaScript will add a semicolon to the end of the return statement.

return; // JS incorrectly adds this semicolon.
{
	a : 'b'; // It'll add a semicolon here as well, because it doesn't realize that this is an object.
};

So, if we immediately return, it has no idea what the property "a" is, thus, resulting in "undefined."

Conclusion

As I mentioned at the beginning of this article, I love JavaScript and use it daily. But that doesn't mean that there aren't some really awful errors in the language. I'd love to hear your thoughts in the comments! Thanks for reading. Retweets and Diggs are always appreciated! Thanks so much to Jeremy McPeak, Doug Crockford, Nicholas Zakas, and John Resig: I referred to your tutorials and books when preparing this article.

Advertisement