The Top 10 Things Wrong with JavaScript

JavaScript has a reputation for being one of the worst programming languages in existence, and for good reasons! JavaScript is easy to learn and easy to use, except when it’s not. There are many “gotchas” that can trip you up. Below, I glean some of the best from various online sources…

1) There is no integer type! JavaScript has only one numerical type and that’s (double precision) floating point. This is the only language I’ve ever used that doesn’t have an integer type (and I’ve used many). Most of the time, you can get away with it, but there will come a day when it will bite you in the ass. Especially if you need 64-bit integers.

0.1 + 0.2 → 0.30000000000000004
0.1 + 0.2 === 0.3 → false // ???
x = 1.0000000000000001
x === 1 → true // ???
typeof NaN → "number" // NaN is a number??? But...
NaN != NaN → true
NaN !== NaN → true

2) JavaScript’s loose typing and aggressive coercions exhibit odd behaviour.

[] + [] → "" // Empty string? These are arrays!
[] + {} → [object object]
{} + [] → 0 // Why isn't the operation commutative???
{} + {} → NaN // ???
16 == [16] → true // Array converted into string, then into number
16 == [1,6] → false // But what is array converted into?
"1,6" == [1,6] → true
var arr = [];
arr.length → "0"
arr[3] → "undefined" // No array bounds exception???
arr[3] = "hi";
arr.length → "4" // 4??? Only one element has been added!
arr["3"] → "hi" // Apparently "3" is coerced into a number
arr.length → "4" // 4??? There are no elements in the array!
arr[3] → "undefined" // 7 lines above, length was "0"!
var i = 1;
i = i + ""; // Oops!
i + 1 → "11"
i - 1 → 0
var j = "1";
j++ → 2 // Okay, but...

var k = "1";
k += 1 → "11" // What???

[1,5,20,10].sort() → [1, 10, 20, 5] // Why is it sorting strings???
xs = ["10", "10", "10"]; → [10, NaN, 2] // ???
y = {};
y[[]] = 1;
Object.keys(y) → [""]

This is nonsense. If a language possesses insane semantics, then it is dangerous to use. If it’s difficult to reason about the language, then it is dangerous to use.

Incidentally, ECMA should remove “==” and force everyone to use “===”. Having both is damn confusing.

3) Automatic semicolon insertion. This can cause subtle bugs and unexpected behaviour. Why does this “feature” even exist??? It’s super weird and unnecessary. Get rid of it, ECMA, please!

4) JavaScript is seriously abused. Much of the code in the wild, especially those in commonly used libraries, are very badly written. The authors have abused the language every which way, and we pay for it in terms of performance and/or ability to reason about the code.

Programmers often have to write workarounds for various problems in the language and these workarounds are immensely complex and cumbersome to understand. For example, the module pattern and other attempts to create private scopes with localized state and logic that use closures and functions wrapped in functions, wrapped in functions, are beyond mad. Thankfully, this is fixed in ES6, but I present it as one of many examples that are still widely prevalent.

This is a language issue because JavaScript’s nature makes it easy, and often necessary, to write convoluted, difficult-to-understand code. Peter DiSalvo goes into more details here, and he is quite eloquent!

5) JavaScript is highly dependent on global variables. Implied global variables are especially problematic (“use strict” to avoid). Global variables seriously complicate your programs.

JavaScript also has horrible scoping rules.

6) JavaScript code can fail silently due to syntactical slip-ups. It has happened to me several times, and tracking down the reason can be most exasperating.

7) Object prototypes do not scale well to large applications; it’s a rather primitive and sloppy way to do object-oriented programming (but it’s flexible!). Further, there are multiple ways to handle object inheritance, making it difficult to decide which way to go. JavaScript is the only popular OOP language that uses object prototypes. The preference for class-based OOP is clear, such that ES6 and TypeScript employ classes. Nevertheless, their classes are not as completely fleshed out as you would find in Smalltalk and C++, for example.

And because the use of object prototypes is so poorly understood by most JavaScript developers, they abuse the language and write horrible code as a result.

8) Asynchronous programming in JavaScript is very messy. Callback hell is a frequent complaint. (Promises mitigate this to some extent, but are not a perfect solution.)

9) Douglas Crockford says that JavaScript is “Lisp in C’s Clothing.” Lisp is a wonderful language and by extension, so is JavaScript.

But JavaScript is nothing like Lisp!

  • JavaScript’s C-like syntax robs it of Lisp’s clean and elegant syntax.
  • Lisp’s central data structure is the list. JavaScript doesn’t have a list data type. JavaScript’s central data structure is the associative array, which often masquerades as some other data type.
  • Lisp is homoiconic, i.e., its code and its data have the same primary representation. JavaScript is not. Not even a little bit.
  • Lisp’s homoiconicity provides for a powerful macro system. JavaScript does not have macros.
  • Lambdas do not make JavaScript Lisp-like, any more than C++, Java, Python, Haskell, and Smalltalk are Lisp-like.

If you like Lisp, then use Lisp. Use Clojure or ClojureScript. Don’t use JavaScript, which has little reason to exist other than its ubiquity in web browsers.

10) The main draw of JavaScript is actually in frameworks like Node.js and AngularJS. If not for them, why would you use JavaScript??? Prior to 2009, when Node.js was released, people generally avoided using JavaScript; its use was limited to some jQuery and webpage enhancements. Everyone understood that JavaScript was a terrible language, and so it was used only sparingly. But then, someone thought it was a good idea to put this awful language on the server.

The front-end JavaScript landscape is highly fragmented and unstable. Frameworks come and go, depending on current trends and whims. AngularJS was the darling of front-end development for a long while, but suddenly ReactJS came out of nowhere to vie for top spot. This makes it dicey to invest a great deal of time and effort in any one particular framework. (The transition from Angular 1.x to 2.0 has been especially disruptive.)

But why use any of these frameworks, only to inflict JavaScript on yourself? Accept JavaScript as the “assembly language” of the Web and select better tools that “transpile” to it. Use Amber. Use ClojureScript. Use Ceylon. Use GopherJS. Hell, use Dart! Use anything but JavaScript.

And guess what? There’s already a de facto standard web framework that’s been around for ages and is rock-solid, well-supported, and widely used. It’s called jQuery and it’s all you really need to create a terrific UX. It’s supported by every transpiled language. Forget Angular, React, Backbone, Ember, Meteor, Polymer, Mithril, Aurelia, etc., etc. etc. Are you kidding me?!!

JavaScript apologists frequently tout using JSLint as a universal solution to JavaScript’s problems. However, this tool has major limitations. It’s also highly opinionated. Moreover, it’s difficult to incorporate into your workflow, which is why many JavaScript developers do not use it. And finally, JSLint’s output results are subject to interpretation and you need to decide what to do about them; sometimes, you actually want to break the “rules!” This additional cognitive burden only makes your job harder, as if programming wasn’t already hard enough. Is it too much to ask for a programming language that doesn’t have such horrendous problems that I need a tool to help me avoid them?