My most hated JavaScript features

Craig Taub
6 min readJun 16, 2018

--

I do love using JavaScript, it is such a powerful flexibly language. However it is not without it faults and here are some of the more annoying ones I have stumbled across in my time with it.

TLDR;

  • Null data type
  • Typeof operator
  • Automatic semicolon insertion
  • Integer support
  • ES6 Class binding
  • Pass-by-ref and lack of Immutability

Null data type

“Clean code” mentions

As soon as you assign NULL you start looking for it

This means you need to include logic checks specifically for Null, and failing to do so could cause fatal issues.

It proposes you should be able to use “function friendly” logic that fits with what you are doing. We should avoid assigning or returning Null as it should remain an indication of a problem.

NULL is falsely in Javascript, but there are several instances where it can easily be buggy:

  1. Default arguments treat Null as valid, i.e. pass Null into someFunc(someParam = ‘default value') and someParam will be Null.
  2. In JSON.stringify Null values are included but undefined values are not.
  3. For libraries such lodash it is a valid value in. i.e. item.get(nullValue, default) will return our constant with Null assigned nullValue. This is because lodash.get only defaults on undefined.

Some languages such as F# and Scala don’t have Null. However the pro’s of having Null tend to outweigh the cons. Hence why so many languages (C/C++ and Java included) use it.

For instance say you wanted to store if a person has hayfever, how would you store it on initialisation?

  • false -> thats a valid “not allergic”
  • 0-> Could also be miscontrued as “not allergic”
  • ""-> Empty string could work but could be confusing
  • Null -> seems to describe it perfectly. “Not yet set”

Basically sometimes you do need to represent nothing, but doesn’t stop it being a pain.

Typeof operator

The main reason this guy is such an issue is because it is so useful, but returns such inconsistent results. For example:

  • typeof([]) or typeof(null) or typeof(/regex/) or typeof(new Date()) ALL equal Object (??)
  • typeof(NaN) (not-a-number) equalsNumber (??). You need to use isNaN(value).

I try and use instanceof more, if its applicable. The only solution to this I’ve found is using the below snippet to create a new type checker.

const​ typeCheck =​ function​ (o) {
var​ s =​ Object.prototype.toString.call(o);
return​ s.match(/\[object (.*?)\]/)[1].toLowerCase();
}
typeCheck({}); // "object"
typeCheck([]); // "array"
typeCheck(5); // "number"
typeCheck(null​); // "null"
typeCheck(); // "undefined"
typeCheck(/abcd/); // "regex"
typeCheck(new​ Date()); // "date"

But rolling your own solution often causes more problems than its worth.

Automatic semicolon insertion

In Douglas Crockford’s booked titled: JavaScript: The Good Parts he goes into detail as to why it is a dangerous feature, I can cover the basics here.

For clarification ASI is when JavaScript inserts a semicolon into a statement as the spec requires all statements be terminated with semicolons. However the engine can do it for you, they are optional.

An example of when this can cause bugs is:

function someFunc() {
return
{
prop: true
};
}
someFunc();
// returns undefined
// WHY -> as ASI has terminated the return statement, meaning the block below it is not returned or run.

It is am implicit feature but error resistance should always be explicit, otherwise it is easy to misunderstand. We should favour syntax that is error resistant and explicit.

Integer support

The short is that JavaScript doesn’t have integers, everything is a 64-bit Floating Point Number with a maximum number of decimals of 17. Unfortunately the prevailing issue is that they have limited precision.

This becomes a problem when doing floating point arithmetic, which produces results which aren’t always right. The famous example is 0.2 + 0.1 !== 0.3. This kind of bug can be especially dangerous when handling money, something which you can’t afford (whey ;) ) to be sloppy with. Other languages make use of Fixed point precision due to this reason.

The solution to this is to use an additional library to help with such calculations. Not great.

I believe ES7 is looking to introduce a BigInt and BigDecimal primitives which will solve this natively. They will represent Integers with arbitrary precision so you can handle numbers beyond the limit of the Number data type.

ES6 Class binding

With ES6 Classes were introduced, internally they are syntactic sugar over prototype-based inheritance.

The problem is that as JavaScript doesn’t yet have semantics where we can easily mark every class method as bound to that class (internally they are objects), there are certain scenarios which do not work.

It is very common for anyone experienced with React (although I think with React you can now auto-bind property methods with myFunction = () => {}).

An example of the problem in a normal Class is:

Class Logger {
printName (name = 'there') {
this.print(`Hello ${name}`);
}
print (text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger;
printName();
// error - can't find print

The solution to this is to bind property methods in the constructor, e.g.

constructor () {
this.printName = this.printName.bind(this);
}

OR to introduce a library such as autobind which will handle this for you automagically. e.g.

constructor() {
autoBind(this);
}

It is possible to also solve this with .call or .apply which allow you to define context.

The problem is all solutions are brittle, tedious and (with Classes) add noise to the constructor.

Pass-by-ref and lack of Immutability

One of the trickiest parts I found when I started with JavaScript was its pass-by-reference approach. For example:

const myObj = { hasGoatee: false };
const myNewObj = myObj;
myNewObj.hasGoatee = true;
console.log(myObj);
// { hasGoatee: true }

The myNewObj constant is now a reference to myObj. This means both constants point to the same address space in memory.

This has a problem in that can lead to all sorts of bugs as its now possible to monkey-patch values onto myObj and you can easily lose context or where certain data was set (or even what somethings value is).

On the other hand it is possible to do very fast comparisons with “referential equality” checks. e.g.

(myObj === myNewObj) // comparison of address space in memory

The best solution is to clone objects first if you are going to mutate them. e.g.

const myObj = { hasGoatee: false };
const myNewObj = Object.assign({}, myObj);
myNewObj.hasGoatee = true;
console.log(myObj);
// { hasGoatee: false }

This ensures that a new object has now been created (in a new address space) with the mutated property on, and the initial object is still available in its original state. This has the benefit that the original object is available to use or lookup if its required.

Unfortunately to compare contents you must do a “deep equality check” which will compare all object property values down its tree. Something which is only possible with a library (such as deep-equal) or to run manually, less performant with below:

JSON.stringify(object1) === JSON.stringify(object2);

The issue is that cloning is a manual process and engineers have to be very careful to do it rather than pass-by-reference, something which can be easily missed.

On the other end of the spectrum to pass-by-ref is Immutability. In a language with Immutable data types, a property is not able to be changed once created and to mutate it you MUST clone it first (often done for you). This allows object comparisons to become referential equality checks just checking memory address location rather than contents. Javascript can create an Immutable object withObject.freeze however you still have to choose your comparison check carefully.

Certain languages enforce this by design and reap several benefits by doing so. One of which is that it fits in perfectly with the Functional programming paradigm as everything is pure and it is not possible to create some of these hard-to-find side-effects.

Overall I really enjoy using JavaScript, it has its faults but it is such a utility belt of functionality.

But with so many alternatives out there its worth getting real experience with others (for me its Python and Go) so you can make up your mind for yourself.

I know JavaScript Dragons are a very contentious area and I am sure there are many I have missed, so please leave a comment if there are any you feel are worth a mention.

Thanks for reading and please spare a clap if you liked this 🙏

More where this came from

This story is published in Noteworthy, where thousands come every day to learn about the people & ideas shaping the products we love.

Follow our publication to see more stories featured by the Journal team.

--

--

Craig Taub
Craig Taub

Responses (1)