You Don't Know JS Yet

Key Takeaways

This is a summary of my favourite points discussed in the course Deep JavaScript Foundations V3 presented by Kyle Simpson on Frontend Masters. I found this course to be invaluable at reexamining key JavaScript concepts that I've used for many years but never explored deep under the hood.

TL:DR Read the JavaScript Specification as it can be an excellent guide to understanding the underlying algorithms that determines JavaScript behaviour

There are occasional "gotchas" in the language to be aware of as they could develop potential bugs in the code. I'll preface those sections with a πŸ›.

JavaScript has a Falsy Values table and everything else coerces to true. Falsy Values Table

Typeof

The typeof operator returns a string indicating the type of the VALUE of a variable. Be careful when evaluated null:

var v = null;
typeof v; // β€œobject”
typeof []; // β€œobject”

πŸ› Perhaps the language should have set typeof null == "null", be careful when performing the following operation so that you don't unintentionally include null or array such as:

typeof v == β€œobject”

πŸ› If a variable has never been declared in any scope, JavaScript will coerce it to undefined. Remember that "undefined" represents a variable that has been declared but doesn't have a value at this moment.

typeof a; // undefined

let a;
typeof a; // "undefined"

NaN

"Not a Number" IEEE 754 spec. A special value that indicates an invalid number.

Number("n/a") // NaN

πŸ› NaN is the only value that does not adhere to the identity property (it is never equal to itself).

NaN === NaN // false

What about isNan?

isNaN(NaN) // true
isNaN("hello how are you!"); // true ... wait what!?

The reason this happens is that isNan() will first coerce the value to a number first which produces NaN:

Number("hello how are you!") // NaN

Source in the specification

πŸ› It's important to think of NaN as an invalid number rather than "Not a Number"

typeof NaN // β€œnumber”

Interesting language quirk: indexOf() returns -1 because back in C, they could only return integers so it needed some way of returning an invalid value and did so with negative 1 and out of tradition this carried over into JavaScript.

Negative Zero ( -0 )

πŸ› In order to comply with IEEE 754, JavaScript needed to implement +0 and -0 as it uses Floating Point arithmetic, but JS actively tries to hide -0 and can cause some really unexpected behaviour:

var trendRate = -0;
trendRate === -0 // true
trendRate.toString(); // β€œ0” OOPS!
trendRate === 0 // true  OOPS!
trendRate < 0 // false  OOPS!
trendRate > 0 // false
Math.sign(-0) // -0  OOPS! (We would expect -1 as Math.sign() returns -1 or 1 for all other valid numbers)

Possible solution, use the ES6 Object.is():

Object.is(trendRate, -0) // true
Object.is(trendRate, 0) // false

Coercion

When you create a literal string, let s2 = String(5) // '5', and then call a method on it, JS will auto-box which converts the string to the wrapper object of String to enable access to all the supporting methods on strings. Try to keep it as String literals as you'll get faster random access speed (it only contains a pointer to a raw memory reference) rather than a robust String object.

πŸ› Be careful around using toString() as it might have some unexpected behaviours:

[null, undefined] => β€œ,” // ignores null and undefined values
[1,2,3].toString() => β€œ1,2,3” // removes array brackets
{}.toString() => β€œ[object Object]” // `toString()` on objects is an inherited method from Object, and should be overridden. If not, it will default to outputting: [object <type>]

POP Quiz! Why does Number([null]) === 0? When JS first tries to apply Number() on an object, it will invoke toPrimitive() with a number hint which in turn consults the valueOf() and then use toString().

[null].toString() => β€œβ€
Number("") => 0

Boolean() has a pretty simple algorithm, anything that doesn't match a value in the Falsy table (see above), will coerce as true.

πŸ› A lot of potential bugs can be avoided by addressing the cases where a value coerces to 0:

Number( β€œβ€ ) => 0
Number( null ) => 0

Be careful around if-checks with multiple operators such as when JS evaluates: if (1 < 2 < 3) {...}

if (1 < 2 < 3) {...}
(1 < 2) => true
(true) < 3 => true // 'true' is getting coerces to a number because of the '<' operator
1 < 3 => true // only coincidence that this returns true

Now what about:
if (3 > 2 > 1) {...}
(3 > 2) => true => 1
if (1 > 1) {...} // FALSE!

Good reminder: A quality JavaScript program embraces coercions, making sure the types involved in every operation are clear and you can safely manage corner cases.

blogs

copyrightΒ©2021 Darren Matis-Mei all rights reserved