Javascript : the bad parts and how to avoid them

The Lone Architect
Sunstack
8 min readSep 16, 2018

--

80% of the pain comes from 20% of the language. It’s time to get rid of them.

I’ve read quite a lot of books about Javascript since the beginning of my career. The following being the most impactful ones :

  • JS : The Good Parts
  • Eloquent Javascript
  • Building Javascript Applications
  • Javascript Design Patterns

And since the start of my adventure, I’ve always loved JS, for many reasons.

  • It’s the de-facto programming language for client-code running in the browser
  • Since around 2009, it can be used as a server programming language, paving the way for isomorphic development
  • It’s dynamic, event-driven, uses an event-loop in a single-thread, which drastically reduces concurrent programming complexity
  • It has an elegant syntax, mostly because it provides functions as first-class citizens (a function is a value like any other, can be passed to functions and returned from them) and closures (a way to access variables in a tight scope, making them private). This is a key feature of composability.
  • It’s prototype-oriented (rather than class-oriented like Java or C++)

But what set it for me is how light it is to write code. Remember the pain it was to declare and manipulate an array in C ? Remember how rigid are C structures ? In Javascript, you have literals, and much of the complexity is managed by the engine, so you’ll never have to deal with it.

That’s great, because you can really focus on your product. You can declare an Array on the spot using literals and fill it with anything : strings, numbers, booleans... Heck, you don’t even care whether it’s a List, a Vector, or a HashTable. Hopefully you’ll never have to know, and people seems happy with it.

Sadly, this design is what gives Javascript a lot of bad parts. Those pain parts are the reasons behind much of the hostility JS is facing nowadays. Hopefully, it is possible to get over them.

Types

Sadly, JS is weakly typed. Variables are untyped, only values are, and what types are they ? Either Object, Symbol, String, Number, Boolean, Null and Undefined. Yes, Null and Undefined are types, and they’re pretty confusing. And ensuring your variable holds an object requires absurd checks like this one :

if (typeof myObject === "object" && myObject !== null)

Because your object can be of type Object, and still be null you know.

Anyway, it is important to ensure your variable holds only one type of content to maintain readability and ease the understanding of the code. Alas, Javascript by itself does nothing to help you, and deep diving into a pure JS codebase can be a nightmare. Remember, it used to be a scripting language. Who would have guessed it’d become a first-class application language ?

How to fix : This problem pushed to community to create TypeScript and FlowType. And to this day I have never come back to raw JS again, because it simply makes no sense. I prefer TypeScript and i’d recommend it, through its complexity is subject to debate (templating someone ?). You can still get most of types using the most basic features of TS, my favorite being optional chaining.

Numbers

What a terrible thing that numbers in JS are represented as double-precision floating points. There are no integer types, so you’ll have to work with them. And here’s the type of jokes it can tell.

For god sake

Because of course, 0.1 + 0.2 is equal to 0.300…4. Which is terrible when you want to check that0.1 + 0.2 === 0.3 because, in JS, it turns out it to be false.

So as a rule of thumb, never make raw float numbers comparison in JS, please.

There’s an explanation to it, but i’d require me to write a whole article about floating point representation, how they are stored in memory and how they are computed. If you’re really curious, you can find tons of articles on the web. Here’s one.

How to fix : Unfortunately, it is difficult to tag it as a bug. But you can work it out by using an other library, such as decimal.js

Coercion

The most painful topic about JS is coercion.

First, what’s coercion ? Recall that JS has native types (Number, String…) and that variables are not typed, values are. When checking typeof myVar === "number" , you are checking the value held in myVar, not the type of the variable itself. I’m emphasising this very important concept.

let myVar = 10; // typeof myVar === "number"
myVar = "Hello World!"; // typeof myVar === "string

Interestingly, what happens when you try to compute two different values ?

let a = 10;
let b = "10";
let c = a + b; // ???

A decimal 10 is represented differently than a string 10. For example, the number 10 could be represented in 8 bits as 00001010. The string 10, is encoded using two ASCII characters* : 00110000 (48) and 00110001 (49).

To handle calculations, the Javascript Engine must ensure both operands are of the same type. And it tries to do the best for you, so in this case it will assume you just want to concatenate a decimal 10 to a string 10, effectively returning a string value of “1010”.

Why, thanks you JS

Be careful when you let the language handle coercion for you, because it’ll come back to bite you. And do you know where this might hurt you the most ?

User Inputs.

Let’s make a scenario : imagine a user wants to invest money in crypto-trading. Because you are rude, you want to take a 1€ fee, so you naively do the following :

let totalCost = 1 + moneyInvested

Then in the afternoon, you receive an e-mail about a lawsuit against your company because the man wanted to invest 100€ and you charged him for 1100€. Are you going to blame this on JS ?

How to fix : never, ever make use of JS coercion mechanism. It just is too painful and it’s rarely a good thing to depend on its implementation. Do it manually :

  • To convert a String to a number, go with parseInt
  • To convert a String to a float, go with parseFloat
  • To convert a Number to a string, use the .toString() method like (54).toString() (yeah that’s funny how native types all are Objects)
  • To convert a String to a Boolean, there’s no magic, you do var myBoolean = myString === "true";

Please, be mostly careful about coercion when it comes to condition and loops. It will bite you. Read this article for more in-depths about it.

Callback hell

To know this has been a thing…

This image says it all about what is callback hell, and why it’s named so. Stacking callbacks on top of callbacks is both painful to read and reason about, and a code smell.

How to fix : of course this has long been solved using various tools :

  • Promises : the real game changer, as you can easily see the flow of your app. You just chain then, passing the value from one callback to another, in a linear way. And you add a single catch to handle whatever terrible things may happen.
  • Async / Await : I said promises were a game changer ? oh whoops. Now this is the real deal. Now you don’t have any more then/catch, your code is just plain old imperative like it has always been, except you add async and await keywords and live as if nothing happened.
  • Generators : yet another way, kind of esoteric some would say, to handle side effects (because yeah, callbacks/promises/async await are really about handling concurrent operations, which are side effects). You might want to check out.

Global Variables

Variables in JS like to hang out open in the air whenever they can. And you know, Javascript really hates to scream at you, and it’ll just clean after your mistakes, all silently. So, you can naively use a variable you didn’t ever declare, and it’s gonna be a global.

function foo() {
x = 10; // Whoops
}
foo();
console.log(x); // 10

And you can access it on the browser through the window object, exposed as window.x . You didn’t even know this variable existed and you still somehow changed it. This is Magicscript.

So, what’s wrong with global variables ?

  • That’s some space allocated you’ll never get back until you nullify them explicitly by calling window.x = null
  • It induces sides effects in the functions that will use them, making the code harder to understand
  • Some libraries depends on global variables, and you might accidentally overwrite them. Worst than that, one could inject malicious code in that global and crash your code or abuse it to leak informations
  • It’s terribly hard to test global variables without mocks and stubs

How to fix it : just never use them. There’s tons of way to get away without using global variables (such as closures and IIFE, I’ll talk about it in an other article). Always use let and const when you declare a variable, and if you’re not confident, use strict.

Wrapping Up

I merely covered a slight part of the bad parts, but those are common enough to be told about. Anyway, please don’t hesitate to give some feedback, and subjects that I should cover. Thanks you !

Through there’s a lot of bad things in JS, recall it has been made in around 10 days, in a hard and competitive climax. The community still managed to take the right path and allowed us to build solid, great apps that are tons of fun to develop. And in my opinion, those so called bad parts are just a small pain point, easy to avoid, completing the overall experience.

*Actually, string encoding in JS is a bit more complicated than that, but that’s an other subject.
*I’ve got notified of comments about the difference between typed variables and typed values. One important feature of JS is its dynamically typed nature : variables can hold any type of value and change at runtime. This means variables are pointer to values, more precisely to records that contain the value’s address and informations about the type. Check the V8 source code for more informations.

--

--

The Lone Architect
Sunstack

Freelance Software Engineer & Architect | Follow me for more articles about System Design, Software Engineering, Distributed Systems, Compilers & Algorithms