The dark side of JavaScript
Written by Milos Aksentijevic 11 October 2019

Dark side of JavaScript

 

JavaScript is one of the most popular programming languages available. It is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions (your functions can be arguments and returned values as much as any variable, or any literal). While it is mostly known as the scripting language for web pages, many non-browser environments also use it, such as Node.js, Apache CouchDB and Adobe Acrobat. It is dynamic and event-driven – uses an event-loop in a single thread, which reduces concurrent programming complexity a lot. It is also prototype-oriented (rather than class-oriented like Java or C++)

JavaScript is a prototype-based, multi-paradigm, dynamic language, supporting object-oriented, imperative, and declarative (e.g. functional programming) styles. It represents the de-facto programming language for client code running in the browser.

The biggest thing to me about JavaScript is how light it is to write code. It is easy to learn and easy to use, except when it’s not. There are many “gotchas” that can trip you up. Sadly, this design is what gives JavaScript a lot of bad parts or how I like to call them “the dark side”.

Let’s cover them.

Types
Sadly, and probably the worst choice ever made about JS, is that it’s weakly typed. Variables are untyped, only values are, and what types are they? It’s either Object, Symbol, String, Number, Boolean, Null (yeah), and Undefined (yeah, undefined is a type of his own). What’s even kind of ridiculous is that when you test that your variable is an object, you have to do this :

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

Because your object can be of type Object, and still be null you know.
It is easy to write code with a dynamically typed language, but it is also easy to create errors. That’s where people started using TypeScript and Flow.

It Can’t Do Arithmetic
Numbers in JavaScript are double-precision floating points. Which means you have no integer type. What’s the problem with it? At a high level, it’s because JavaScript used the IEEE Standard for Binary Floating-Point Arithmetic

0.1 + 0.2 = 0.30000000000000004

Coercion
The most painful topic about JS is coercion. This is what all should be aware of.
Recall that JS has native types (Number, String… etc) and that variables are not typed, values are. When you do:
typeof myVar === "number"
You are actually looking to know if the type of the value myVar is pointing to is a number. It’s important to understand that.

Now, what happens when you want to do an operation with two variables, whose values are different types?

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 do a proper calculation, the JavaScript Engine must ensure both operands are of the same type. And JS 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, and returns a string value of “1010”.

You have to be very careful. And you know where this might hurt you the most? Here are some coercion examples:

[] + [] → "" // 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

Coercion
CallBack hell
Callbacks are just the name of a convention for using JavaScript functions. There isn’t a special thing called a ‘callback’ in the JavaScript language, it’s just a convention. Instead of immediately returning some result like most functions, functions that use callbacks take some time to produce a result. The word ‘asynchronous’, aka ‘async’, just means ‘takes some time’ or ‘happens in the future, not right now’. Usually, callbacks are only used when doing I/O, e.g. downloading things, reading files, talking to databases, etc. Nesting callbacks (function calls) will create an issue for you without knowing what function and when will be executed (callback-hell)

Solution for this is using promises, generators, async-await.

Global variables
Why global variables are wrong?
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 your functions that are going to use that, which makes the code way more complex to understand
Other people could as well have used this variable for a library or something (which isn’t rare for browser UI libs). Worst than that, one could inject malicious code in that global that would make your code crash or worst, leak pieces of information.
It’s hard to test global variables because they are global, and their usage is hard to determine.

There’s tons of way to get away without using global variables (such as closures and IIFE). Always use let and const when you declare a variable, and if you’re not confident, use strict.

Scope Inconsistencies
Developer should be aware of using scopes. Especially when “this” comes in question. Every function, every object create their own scope, and some variables or other functions might not be available at some parts of the code. Therefore you need to be careful about inner and outer scope of some object/function

Talking about this keyword, developers often make mistakes using this in arrow functions.

const foo = () => {
let a = 10;
console.log(this)
}

foo() -> // this will refer to global (outer scope).

The reason why is this happening is that arrow functions do not have this.

Support for other ES versions
There are a lot of bugs covered in new ES versions of the language. But to have support for older libraries and scripts, browsers support older versions, and therefore all older sites could “live”.

The advice and solution for this is using the newest versions like ES6+ with ‘use strict’ directive on top of your code or top of function body that you write.

When it is located at the top of a script, the whole script works the “modern” way. The “use strict” directive switches the engine to the “modern” mode, changing the behaviour of some built-in features. Several language features, like “classes” and “modules”, enable strict mode automatically. Strict mode is supported by all modern browsers. Without “use strict”, everything still works, but some features behave in the old-fashion, “compatible” way. We’d generally prefer modern behaviour.

 

 

I love JavaScript and use it daily. But that doesn’t mean that there aren’t some really awful errors in the language. I think I didn’t even closely cover every bad thing in JS and how to avoid them, but I want this list to be updated as much as possible, so please don’t hesitate to give some feedback, and subjects that I should cover. Thank you for reading!

BACK

LEAVE A COMMENT

JOIN THE MAILING LIST