Reading JavaScript
six places where Python intuition breaks
What it is
JavaScript resembles Python just closely enough to mislead. Loops, functions and conditions look familiar — and then the code does something else, and it isn't clear why.
This article covers six places where intuition lets you down. Everything
below is real node output, not a retelling of the documentation.
What it looks like in code
1. Sorting numbers doesn't sort numbers
[10, 9, 1, 100].sort()[ 1, 10, 100, 9 ]By default sort turns the elements into strings and compares them as
text. Numbers need a comparison function:
[10, 9, 1, 100].sort((a, b) => a - b) // [ 1, 9, 10, 100 ]The mistake is silent: with numbers from 1 to 9 you don't notice it; with two digits it breaks.
2. Two kinds of equality
'1' == 1 -> true
'1' === 1 -> false
null == undefined -> true
null === undefined -> false== converts types before comparing, === doesn't. The rule is simple:
always ===. The one justified exception is x == null, which checks
for null and undefined in a single go.
3. Truthiness is not what you're used to
[] -> truthy (in Python an empty list is false!)
{} -> truthy
"" -> falsy
"0" -> truthy
0 -> falsy
NaN -> falsy
null -> falsy
undefined -> falsyThe first line breaks more code than any other. The "are there any items"
check written as if (items) is always true in JavaScript, even for an
empty array. It has to be if (items.length).
4. Plus concatenates, minus subtracts
1 + '1' -> '11' (joined the strings)
1 - '1' -> 0 (converted to numbers)Hence the classic: the data arrived from a form or from JSON as strings, and
instead of a sum you get a join. '10' + 5 gives '105'.
5. Two kinds of "nothing"
| What it means | |
|---|---|
undefined | there was no value: no such key, the function returned nothing |
null | there is a value, and it is "nothing" |
Reaching for a key that doesn't exist doesn't fail:
user.email -> undefined
user.email?.length -> undefined
user.email ?? 'not set' -> 'not set'This is the opposite of Python, where that gives a KeyError. Convenient
and dangerous at once: the mistake doesn't announce itself, it surfaces
later as Cannot read properties of undefined (article 8.4).
Two operators you will meet constantly:
?.— "if the left side isn'tnull/undefined, keep going, otherwise returnundefined";??— "take the left one, and if it isnull/undefined, take the right one".
?? differs from || in not treating 0 and "" as emptiness. For
numbers that is decisive: temp || 20 turns zero degrees into twenty,
temp ?? 20 doesn't.
6. const doesn't make an object immutable
const cities = ["Oslo"];
cities.push("Bergen"); // allowed
cities = []; // not allowedconst forbids reassigning the variable, not changing the contents. The
same thing as references in Python (article 2.4).
What to tell the AI
Rewrite this code with strict comparisons: === everywhere
instead of ==, array checks through .length, ?? instead of ||
where the value may be 0 or an empty string.
Don't change the behaviour.This code came from Python. What will behave differently here
because of JavaScript's quirks? Just the list of differences.The numbers arrive from JSON as strings. Where in this code
could a concatenation happen instead of an addition?What to look for
== in generated code. It should almost always be ===.
sort() without a comparison function on numbers.
if (array) instead of if (array.length). The condition is always
true.
|| for default values. Breaks on 0 and "". You want ??.
var. An outdated declaration with surprising scope. New code uses only
let and const (article 3.1, reason 3).
Numbers from strings without conversion. Number(x) or
parseInt(x, 10) — and always with the base 10.
Cheat sheet
sort()sorts as strings by default:[1, 10, 100, 9].Always
===. The exception isx == nullfor both emptinesses at once.An empty array is truthy. Check
.length.+with a string concatenates,-converts to numbers.?.is safe access,??is a default value (not to be confused with||).constforbids reassignment, not mutation.