In recent years, my professional work has shifted towards JavaScript (JS). Before that, I used Java (Spring Boot/Swing/In-house 3D engine), C# (Unity) and sometimes even C++. It was strange at first, but after a while, using JavaScript felt like a natural transition. I like the module system a lot, and also the Browser, as an UI runtime that actually runs on billions of devices, is extremely useful.

Naturally, as a programmer, when working with a language for some time you think that you know the bigger issues. You feel comfortable even though JavaScript can feel like a minefield at times.

Have a look at this:

[] + {};   -> "[object Object]"
{} + [];   -> 0
'5' - 3    -> 2
'5' + 3    -> "53"
typeof NaN -> "number"

They might look strange and could be a nice topic for a JavaScript-bashing clip on TikTok. But in practice, none of them ever got me. Yes, I encountered the NaN thing, but it’s nothing too wild.

However, something that felt like a shock to me was the sorting numbers issue. In short this:

[3, 20, 1].sort() -> [1, 20, 3]

What happens here is well known, but I must have missed that. The sort methode of a JavaScript array cannot know what type of objects live inside it. So, there can be anything in there. To solve this, they went for a generic approach:

“[…] JavaScript compares the sequences of UTF-16 code unit values” MDN Array.prototype.sort()

In other words, this:

// kind of what happens:
[3, 20, 1]
    .map(e => {return {sortVal: e.toString(), orgVal: e}})
    .sort((a, b) => compareUtf16Codes(a.sortVal, b.sortVal))
    .map(e => e.orgVal)

// (obviously the JS engine does this more efficiently under the hood)

For a number array, this looks like a crazy solution, but it does make a lot of sense when we change the content of the array:

[1, "hello", [5,4,6], {x: 1, y: 2, z: 5}].sort();

What shall sort do in such a case? Convert them all to numbers? Scan the array first if they all match a certain type and pick the best sorting algorithm based on that type? Enforce a type on arrays?

Sometimes, there is no perfect solution, so they went for a compromize and opted for the string-conversion solution.

:-) I created a nice little bug with this that rarely surfaced as I compared unix timestamps, which happen to have a constant-ish string lengths. It all fell apart when I introduced a hack where I used an index instead of timestamp values. Suddenly, none of the data made any sense even though the same code worked for years in production before.


PS: To compare number, this can help:

const compareNumbers = (a, b) => a - b;
[3, 20, 1].sort(compareNumbers) // < solved ;-)