Handbooks / JavaScript / Chapter 1
Types and Coercion
7 pages · ~5 min✓ Reviewed
Start here. Next up: Scope and Closures.
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 = ais an independent copy - Objects are stored by reference:
b.x = 1changesa.xtoo 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
| Primitives | Objects | |
|---|---|---|
let b = a | independent copy | one shared object |
=== | by value: 'a'==='a' true | by identity: {}==={} false |
const o = {}; o.x = 1works:constfreezes the binding only{...o}is shallow, nested objects stay shared; usestructuredClone(o)
- Interview: why is
[1]===[1]false? Two separate objects;===compares identity. - Does
constmake an object immutable? No, only the binding. - Shallow vs deep copy? Spread copies one level;
structuredClonecopies nested too.
typeof Quirks
1. What typeof returns
typeofalways returns a string, never a type object- Primitives:
'string''number''bigint''boolean''undefined''symbol' - The other two:
'object'and'function'
2. The traps
| Value | typeof | Right 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:
typeofgives'undefined', no error let/constin its temporal dead zone: throwsReferenceError
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.
vs =
1. Strict vs Loose
===compares type and value, no conversion==converts operands first (loose equality)- Default to
=== null == undefinedis true; nothing else equals them- So
x == nulltests both in one go
2. Loose Rules and Surprises
- String vs numberstring → number: '5'==5
- Boolean→ number first: true==1
- Object vs primitiveToPrimitive: [1]==1
| Expression | Result | Why |
|---|---|---|
'' == 0 | true | '' → 0 |
'0' == false | true | both → 0 |
'' == '0' | false | two strings |
[] == false | true | '' → 0 |
[] == ![] | true | both → 0 |
null == 0 | false | only undefined |
3. Object.is
| Pair | === | Object.is |
|---|---|---|
NaN, NaN | false | true |
0, -0 | true | false |
Not transitive
'' == 0 and 0 == '0' are true, yet '' == '0' is false.
Truthy, Falsy and Coercion Rules
1. Truthy and Falsy
- Exactly 8 falsy:
false0-00n''nullundefinedNaN - Everything else is truthy
- Surprise truthy:
'0''false'[]{}function(){}new Boolean(false)-1Infinity
2. Coercion
| Value | Number() | Value | Number() |
|---|---|---|---|
'' | 0 | null | 0 |
' 12 ' | 12 | undefined | NaN |
'abc' | NaN | true | 1 |
[] | 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 onlynullandundefined
javascript
0 || 5 // 5 0 ?? 5 // 0
Defaults
Use ?? when 0 or '' are valid values.
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
| undefined | null | |
|---|---|---|
typeof | 'undefined' | 'object' |
Number(x) | NaN | 0 |
JSON.stringify | property dropped | kept |
x + 1 | NaN | 1 |
2. NaN
- Type number, made by failed maths
0/0,Number('abc'),parseInt('x'),Math.sqrt(-1)- Only value not equal to itself:
NaN === NaNis false Number.isNaN(x)is strict; globalisNaNcoerces 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
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
| Value | typeof | Test 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 forx == 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 ''.
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.aando.aare the same objectstructuredClone(o)gives a deep copy
Why is [] == ![] true?
![]isfalse, because arrays are truthy[] == false: false becomes 0,[]becomes''then 00 == 0is true
What do 0 || 5 and 0 ?? 5 give?
5and0||skips any falsy value??skips only null and undefined