Handbooks / JavaScript / Chapter 1

Types and Coercion

7 pages · ~5 min✓ Reviewed

Start here. Next up: Scope and Closures.

JavaScriptPage 1

Primitives vs Reference Types

1. The Two Families

  • 7 primitives: string, number, bigint, boolean, undefined, null, symbol
  • Everything else is an object: arrays, functions, dates, maps

2. Copy vs Share

  • Primitives are immutable; let b = a is an independent copy
  • Objects are stored by reference: b.x = 1 changes a.x too
  • s[0]='X' is ignored in sloppy mode, TypeError in strict (modules, classes)
javascript
let a = 1, b = a; b = 2;
const o = {x: 0}, p = o; p.x = 1;
console.log(a, o.x, [1] === [1]);
output
1 1 false

3. Compare and Mistakes

PrimitivesObjects
let b = aindependent copyone shared object
===by value: 'a'==='a' trueby identity: {}==={} false
  • const o = {}; o.x = 1 works: const freezes the binding only
  • {...o} is shallow, nested objects stay shared; use structuredClone(o)
  • Interview: why is [1]===[1] false? Two separate objects; === compares identity.
  • Does const make an object immutable? No, only the binding.
  • Shallow vs deep copy? Spread copies one level; structuredClone copies nested too.
JavaScriptPage 2

typeof Quirks

1. What typeof returns

  • typeof always returns a string, never a type object
  • Primitives: 'string' 'number' 'bigint' 'boolean' 'undefined' 'symbol'
  • The other two: 'object' and 'function'

2. The traps

ValuetypeofRight test
null'object' (legacy bug)x === null
[]'object'Array.isArray(x)
new Date()'object'x instanceof Date
function(){}'function'still an object
class A{}'function'a class is a function

3. Undeclared vs TDZ

  • Undeclared name: typeof gives 'undefined', no error
  • let/const in its temporal dead zone: throws ReferenceError
javascript
typeof nope;            // 'undefined'
{ typeof x; let x = 1 } // ReferenceError

typeof is only safe for names never declared

Remember

typeof cannot tell null, arrays or dates from plain objects. Use === null and Array.isArray.

JavaScriptPage 3

vs =

1. Strict vs Loose

  • === compares type and value, no conversion
  • == converts operands first (loose equality)
  • Default to ===
  • null == undefined is true; nothing else equals them
  • So x == null tests both in one go

2. Loose Rules and Surprises

  1. String vs numberstring → number: '5'==5
  2. Boolean→ number first: true==1
  3. Object vs primitiveToPrimitive: [1]==1
ExpressionResultWhy
'' == 0true'' → 0
'0' == falsetrueboth → 0
'' == '0'falsetwo strings
[] == falsetrue'' → 0
[] == ![]trueboth → 0
null == 0falseonly undefined

3. Object.is

Pair===Object.is
NaN, NaNfalsetrue
0, -0truefalse
Not transitive

'' == 0 and 0 == '0' are true, yet '' == '0' is false.

JavaScriptPage 4

Truthy, Falsy and Coercion Rules

1. Truthy and Falsy

  • Exactly 8 falsy: false 0 -0 0n '' null undefined NaN
  • Everything else is truthy
  • Surprise truthy: '0' 'false' [] {} function(){} new Boolean(false) -1 Infinity

2. Coercion

ValueNumber()ValueNumber()
''0null0
' 12 '12undefinedNaN
'abc'NaNtrue1
[]0[5]5
{}NaN
  • + with any string concatenates: 1+'2' is '12', [1]+[2] is '12'
  • - * / % always convert to number: '6'-'2' is 4, '3'*'4' is 12

3. || && ??

  • || returns the first truthy operand
  • && returns the first falsy operand
  • ?? skips only null and undefined
javascript
0 || 5   // 5
0 ?? 5   // 0
Defaults

Use ?? when 0 or '' are valid values.

JavaScriptPage 5

null, undefined and NaN

1. undefined vs null

  • undefined = no value assigned: uninitialised variable, missing property or argument, no return
  • null = intentional empty value, set by the programmer
undefinednull
typeof'undefined''object'
Number(x)NaN0
JSON.stringifyproperty droppedkept
x + 1NaN1

2. NaN

  • Type number, made by failed maths
  • 0/0, Number('abc'), parseInt('x'), Math.sqrt(-1)
  • Only value not equal to itself: NaN === NaN is false
  • Number.isNaN(x) is strict; global isNaN coerces first
javascript
Number.isNaN('abc')  // false
isNaN('abc')         // true
[NaN].includes(NaN)  // true
[NaN].indexOf(NaN)   // -1

3. Mistake: NaN spreads

One NaN poisons the maths

Any arithmetic with NaN or undefined gives NaN. Guard with ?. and ??: user?.age ?? 0

JavaScriptPage 6

Types and Coercion Cheat Sheet

1. Types and typeof traps

  • 7 primitives (string, number, bigint, boolean, undefined, null, symbol): copied by value
  • Objects, arrays, functions: by reference; === tests identity, so {}==={} is false
  • s[0]='X' is ignored in sloppy mode, a TypeError in strict mode
ValuetypeofTest with
null'object'x === null
array'object'Array.isArray(x)
function'function'typeof

2. Truthiness and coercion

  • Falsy (exactly 8): false 0 -0 0n '' null undefined NaN
  • Everything else is truthy, including '0', [] and {}
  • + with any string concatenates: 1+'2' is '12'
  • - * / % convert to number: '6'-'2' is 4
  • == only for x == null (null and undefined); else ===

3. Checks

javascript
Number.isNaN('abc')  // false
isNaN('abc')         // true (coerces)
Object.is(0, -0)     // false
0 || 5               // 5
0 ?? 5               // 0

NaN and -0 checks; defaults

NaN

NaN === NaN is false. Use Number.isNaN or Object.is; ?? keeps 0 and ''.

JavaScriptPage 7

Check yourself

With o = {a:{b:1}} and const c = {...o}, you run c.a.b = 2. What is o.a.b?
  • 2: spread is shallow
  • c.a and o.a are the same object
  • structuredClone(o) gives a deep copy
Why is [] == ![] true?
  • ![] is false, because arrays are truthy
  • [] == false: false becomes 0, [] becomes '' then 0
  • 0 == 0 is true
What do 0 || 5 and 0 ?? 5 give?
  • 5 and 0
  • || skips any falsy value
  • ?? skips only null and undefined