Boolean, Undefined, Null & NaN — Three "Nothings"
Teeno ka matlab alag hai — mix mat karo bhai, interview mein pakde jaoge.
In V8, true and false are singleton HeapObjects — created once at startup and reused everywhere. This means true === true is a pointer comparison: one CPU instruction.
typeof true === 'boolean' and typeof false === 'boolean' — straightforward. But the Boolean wrapper is a trap: new Boolean(false) creates an object, not a primitive. Objects are always truthy!
// Primitive booleans — singletons
console.log(true === true); // true — same pointer
console.log(false === false); // true — same pointer
console.log(typeof true); // 'boolean'
// The Boolean wrapper trap
const boolObj = new Boolean(false); // an OBJECT wrapping false
console.log(typeof boolObj); // 'object' — not 'boolean'!
console.log(!!boolObj); // true — objects are always truthy!
if (boolObj) {
console.log('This runs!'); // new Boolean(false) is truthy!
}
// Boolean() conversion — no 'new'
console.log(Boolean(0)); // false
console.log(Boolean('')); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean('0')); // true — non-empty string!
console.log(Boolean([])); // true — empty array!
console.log(Boolean({})); // true — empty object!
// Short circuit — returns VALUES not just true/false
console.log(1 && 2); // 2 — last value if all truthy
console.log(0 && 2); // 0 — first falsy
console.log(null || 'default'); // 'default' — first truthy
console.log(0 || 'default'); // 'default' — 0 is falsy!
console.log(0 ?? 'default'); // 0 — ?? only checks null/undefined
undefined is both a primitive value AND a global property. V8 uses it when no value has been assigned to a variable.
Cases that produce undefined: uninitialized var, missing function argument, function with no return, accessing a non-existent object property, the void operator.
typeof undefined === 'undefined' — the safest typeof check. And undefined is NOT a reserved word in old JS — it could be reassigned (now blocked in strict mode). That is why void 0 === undefined was used as a safe substitute — still seen in minified code.
// When does undefined appear?
let x;
console.log(x); // undefined — var declared but not assigned
function noReturn() {}
console.log(noReturn()); // undefined — no return statement
function withParam(a, b) {
console.log(b); // undefined — b not provided
}
withParam(1);
const obj = { x: 1 };
console.log(obj.y); // undefined — property doesn't exist
console.log(obj.x.z); // TypeError! — can't access .z on number
// typeof check — the only safe check for undefined:
let val;
console.log(typeof val === 'undefined'); // true ✅ — safe
console.log(val === undefined); // true ✅ — also safe now
// console.log(val === void 0); // true — old minifier trick
// Difference: null vs undefined
console.log(null == undefined); // true — loose equality
console.log(null === undefined); // false — strict equality
console.log(typeof null); // 'object' — the famous bug
console.log(typeof undefined); // 'undefined' — correct
// Optional chaining produces undefined (not error):
const user = null;
console.log(user?.name); // undefined — no crash
console.log(user?.name ?? 'Guest'); // 'Guest' — nullish coalescing
undefined signals "this variable/property exists but has no value yet". null signals "this variable exists and intentionally has no value". This distinction matters for API design: a function returning undefined means "nothing was found", returning null means "I checked and there is nothing there" — the programmer explicitly set it.
null represents the intentional absence of a value — the programmer explicitly set it. But typeof null === 'object' — the 1995 bug that is now permanent in the ECMAScript spec.
In V8's original C implementation, null had the same type tag as objects (0x00). A proposal to fix this (typeof null === 'null') was rejected in ES2015 to avoid breaking the web. null is a primitive (not an object), despite what typeof says.
null has no prototype — null.toString() is a TypeError. Both ?? and ?. treat null the same as undefined.
// The type bug
console.log(typeof null); // 'object' — WRONG, but spec-defined
console.log(null === null); // true — same singleton
// null is NOT an object despite typeof
try {
null.toString(); // TypeError: Cannot read properties of null
} catch(e) {
console.log(e.message); // Cannot read properties of null
}
// Correct null checks — NEVER use typeof for null
console.log(null === null); // ✅ strict equality
console.log(null == null); // ✅ loose equality
console.log(null == undefined);// ✅ catches both null and undefined
// null in prototype chain — the end of the chain
console.log(Object.getPrototypeOf(Object.prototype)); // null
// The prototype chain terminates at null — not undefined!
// Historical reason for the bug (V8 internals):
// In the original C code:
// #define VALUE_NULL 0x00000000
// #define TAG_OBJECT 0x00000000 ← same tag!
// typeof checked the tag — null and objects had same tag
// Fixing it would break: if (typeof myVar === 'object') — null would escape
// Practical: use null to explicitly "reset" a reference
let activeUser = { name: 'Sai' };
// ... later:
activeUser = null; // explicitly clear — more intentional than undefined
NaN stands for "Not a Number" — but typeof NaN === 'number'! It is a number that failed to be computed, defined by the IEEE 754 floating-point standard.
IEEE 754 defines multiple NaN bit patterns (all have exponent=all-1s, non-zero mantissa). V8 uses a canonical NaN with sign=0, exponent=all-1s, and a specific mantissa pattern.
NaN !== NaN — the ONLY value in JS not equal to itself (IEEE 754 requirement). NaN also propagates: any arithmetic with NaN gives NaN.
// How NaN is created
console.log(0 / 0); // NaN
console.log(Infinity - Infinity); // NaN
console.log(parseInt('hello')); // NaN
console.log(Math.sqrt(-1)); // NaN
console.log(undefined + 1); // NaN — undefined converts to NaN
// NaN's self-inequality — the most unique JS behavior
console.log(NaN === NaN); // false — unique!
console.log(NaN !== NaN); // true
console.log(NaN < 1); // false
console.log(NaN > 1); // false
console.log(NaN == NaN); // false — even loose equality!
// NaN propagates through arithmetic
console.log(NaN + 1); // NaN
console.log(NaN * Infinity); // NaN
console.log(Math.max(1, NaN, 3));// NaN — NaN poisons the result
// NaN in arrays — indexOf can't find it!
const arr = [1, NaN, 3];
console.log(arr.indexOf(NaN)); // -1 — uses === which fails for NaN
console.log(arr.includes(NaN)); // true — uses SameValueZero, handles NaN ✅
console.log(arr.findIndex(x => Number.isNaN(x))); // 1 ✅
// Correct NaN check:
console.log(Number.isNaN(NaN)); // true ✅ — exact
console.log(Number.isNaN('hello')); // false ✅ — no coercion
console.log(isNaN('hello')); // true ❌ — coerces string!
console.log(Number.isNaN(undefined)); // false ✅ — undefined is not NaN
Each "nothing" has its own correct check. Using the wrong one causes silent bugs.
Checking undefined: typeof x === 'undefined' (safe even if x undeclared) OR x === undefined (simpler if x exists). Checking null: x === null (ONLY correct way). Checking null OR undefined: x == null (double equals catches both). Checking NaN: Number.isNaN(x) (ONLY correct way). Checking nullish (not 0 or false): x ?? 'default'.
// Comprehensive check guide:
function checkValue(x) {
// Is it undefined?
if (typeof x === 'undefined') console.log('undefined (typeof safe)');
if (x === undefined) console.log('undefined (direct)');
// Is it null?
if (x === null) console.log('null');
// Is it null OR undefined?
if (x == null) console.log('null or undefined (loose ==)');
// Is it NaN?
if (Number.isNaN(x)) console.log('NaN');
// Is it ANY falsy value?
if (!x) console.log('falsy (0, "", null, undefined, NaN, false)');
// Is it a "missing value" (null or undefined) but NOT 0 or false?
const result = x ?? 'default';
if (result === 'default') console.log('nullish (null or undefined)');
}
checkValue(null); // null, null or undefined, falsy, nullish
checkValue(undefined); // undefined, null or undefined, falsy, nullish
checkValue(NaN); // NaN, falsy (NaN is falsy!)
checkValue(0); // falsy only! — ?? gives 0, not 'default'
checkValue(''); // falsy only!
checkValue(false); // falsy only!
// The ?? vs || difference — crucial
const count = 0;
console.log(count || 'no items'); // 'no items' — 0 is falsy!
console.log(count ?? 'no items'); // 0 — 0 is not nullish
// Use ?? when 0, '', false are valid values
Use
x == null (double equals) to check for "null or undefined". This is one of the rare cases where loose equality is correct and intentional — it catches exactly null and undefined, nothing else. For everything else, use strict equality. This replaces: x === null || x === undefined.Lo kar liya — Key Points:
- ✅
new Boolean(false)is an OBJECT — always truthy. UseBoolean()without new for conversion - ✅ undefined = "value was never assigned". null = "intentionally no value". Different semantics — use them correctly
- ✅
typeof null === 'object'— a 1995 C implementation bug, now permanent in the ECMAScript spec - ✅
NaN !== NaN— the only value in JS not equal to itself. UseNumber.isNaN(), neverisNaN()(coerces) - ✅ NaN propagates: any operation with NaN gives NaN. Use
includes()notindexOf()to find NaN in arrays - ✅
x == nullcatches BOTH null and undefined — use this intentional loose equality for existence checks
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login