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 ;-)