Back to 33 Js Concepts

Primitive Types

docs/concepts/primitive-types.mdx

latest43.0 KB
Original Source

What's the difference between "hello" and { text: "hello" }? Why can you call "hello".toUpperCase() if strings aren't objects? And why does typeof null return "object"?

javascript
// JavaScript has exactly 7 primitive types
const str = "hello";           // string
const num = 42;                // number
const big = 9007199254740993n; // bigint
const bool = true;             // boolean
const undef = undefined;       // undefined
const nul = null;              // null
const sym = Symbol("id");      // symbol

console.log(typeof str);   // "string"
console.log(typeof num);   // "number"
console.log(typeof nul);   // "object" — Wait, what?!

These seven primitive types are the foundation of all data in JavaScript. Unlike objects, primitives are immutable (unchangeable) and compared by value. Every complex structure you build (arrays, objects, classes) ultimately relies on these simple building blocks.

<Info> **What you'll learn in this guide:** - The 7 primitive types in JavaScript and when to use each - How `typeof` works (and its famous quirks) - Why primitives are "immutable" and what that means - The magic of autoboxing — how `"hello".toUpperCase()` works - The difference between `null` and `undefined` - Common mistakes to avoid with primitives - Famous JavaScript gotchas every developer should know </Info> <Note> **New to JavaScript?** This guide is beginner-friendly! No prior knowledge required. We'll explain everything from the ground up. </Note>

What Are Primitive Types?

In JavaScript, a primitive is data that is not an object and has no methods of its own. As defined by the ECMAScript 2024 specification (ECMA-262), JavaScript has exactly 7 primitive types:

TypeExampleDescription
string"hello"Text data
number42, 3.14Numeric data (integers and decimals)
bigint9007199254740993nVery large integers
booleantrue, falseLogical values
undefinedundefinedNo value assigned
nullnullIntentional absence of value
symbolSymbol("id")Unique identifier

Three Key Characteristics

All primitives share these fundamental traits:

<AccordionGroup> <Accordion title="1. Immutable - Values Cannot Be Changed"> Once a primitive value is created, it cannot be altered. When you "change" a string, you're actually creating a new string.
```javascript
let name = "Alice";
name.toUpperCase();    // Creates "ALICE" but doesn't change 'name'
console.log(name);     // Still "Alice"
```
</Accordion> <Accordion title="2. Compared By Value"> When you compare two primitives, JavaScript compares their actual values, not where they're stored in memory.
```javascript
let a = "hello";
let b = "hello";
console.log(a === b);  // true - same value

let obj1 = { text: "hello" };
let obj2 = { text: "hello" };
console.log(obj1 === obj2);  // false - different objects!
```
</Accordion> <Accordion title="3. No Methods (But Autoboxing Magic)"> Primitives don't have methods, but JavaScript automatically wraps them in objects when you try to call methods. This is called "autoboxing."
```javascript
"hello".toUpperCase();  // Works! JS wraps "hello" in a String object
```

We'll explore this magic in detail later.
</Accordion> </AccordionGroup>

The Atoms vs Molecules Analogy

Think of data in JavaScript like chemistry class (but way more fun, and no lab goggles required). Primitives are like atoms: the fundamental, indivisible building blocks that cannot be broken down further. Objects are like molecules: complex structures made up of multiple atoms combined together.

┌─────────────────────────────────────────────────────────────────────────┐
│                     PRIMITIVES VS OBJECTS                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   PRIMITIVES (Atoms)                    OBJECTS (Molecules)              │
│                                                                          │
│   ┌───┐  ┌─────┐  ┌──────┐             ┌────────────────────────────┐   │
│   │ 5 │  │"hi" │  │ true │             │ { name: "Alice", age: 25 } │   │
│   └───┘  └─────┘  └──────┘             └────────────────────────────┘   │
│                                                                          │
│   • Simple, indivisible                 • Complex, contains values       │
│   • Stored directly                     • Stored as reference            │
│   • Compared by value                   • Compared by reference          │
│   • Immutable                           • Mutable                        │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Just like atoms are the foundation of all matter, primitives are the foundation of all data in JavaScript. Every complex data structure you create — arrays, objects, functions — is ultimately built on top of these simple primitive values.


The 7 Primitive Types: Deep Dive

Let's explore each primitive type in detail.


String

A string represents text data: a sequence of characters.

javascript
// Three ways to create strings
let single = 'Hello';           // Single quotes
let double = "World";           // Double quotes
let backtick = `Hello World`;   // Template literal (ES6)

Template Literals (Still Just Strings!)

Template literals (backticks) are not a separate type. They're just a more powerful syntax for creating strings. The result is still a regular string primitive:

javascript
let name = "Alice";
let age = 25;

// String interpolation - embed expressions
let greeting = `Hello, ${name}! You are ${age} years old.`;
console.log(greeting);        // "Hello, Alice! You are 25 years old."
console.log(typeof greeting); // "string" — it's just a string!

// Multi-line strings
let multiLine = `
  This is line 1
  This is line 2
`;
console.log(typeof multiLine); // "string"

Strings Are Immutable

You cannot change individual characters in a string:

javascript
let str = "hello";
str[0] = "H";        // Does nothing! No error, but no change
console.log(str);    // Still "hello"

// To "change" a string, create a new one
str = "H" + str.slice(1);
console.log(str);    // "Hello"
<Tip> String methods like `toUpperCase()`, `slice()`, `replace()` always return **new strings**. They never modify the original. </Tip>

Number

JavaScript has only one number type for both integers and decimals. All numbers are stored as 64-bit floating-point (a standard way computers store decimals).

javascript
let integer = 42;        // Integer
let decimal = 3.14;      // Decimal
let negative = -10;      // Negative
let scientific = 2.5e6;  // 2,500,000 (scientific notation)

Special Number Values

javascript
console.log(1 / 0);       // Infinity
console.log(-1 / 0);      // -Infinity
console.log("hello" * 2); // NaN (Not a Number)

JavaScript has special number values: Infinity for values too large to represent, and NaN (Not a Number) for invalid mathematical operations.

The Famous Floating-Point Problem

javascript
console.log(0.1 + 0.2);           // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);   // false! Welcome to JavaScript!

This isn't a JavaScript bug — it follows the IEEE 754 double-precision floating-point standard used by virtually all modern programming languages. The decimal 0.1 cannot be perfectly represented in binary.

<Warning> **Working with money?** Never use floating-point for calculations! Store amounts in cents as integers, then use JavaScript's built-in [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) for display.
javascript
// Bad: floating-point errors in calculations
let price = 0.1 + 0.2;  // 0.30000000000000004

// Good: calculate in cents, format for display
let priceInCents = 10 + 20;  // 30 (calculation is accurate!)

// Use Intl.NumberFormat to display as currency
const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
});
console.log(formatter.format(priceInCents / 100));  // "$0.30"

// Works for any locale and currency!
const euroFormatter = new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: 'EUR',
});
console.log(euroFormatter.format(1234.56));  // "1.234,56 €"
</Warning> <Tip> `Intl.NumberFormat` is built into JavaScript. No external libraries needed! It handles currency symbols, decimal separators, and locale-specific formatting automatically. </Tip>

Safe Integer Range

JavaScript can only safely represent integers up to a certain size:

javascript
console.log(Number.MAX_SAFE_INTEGER);  // 9007199254740991 (2^53 - 1)
console.log(Number.MIN_SAFE_INTEGER);  // -9007199254740991

Number.MAX_SAFE_INTEGER is the largest integer that can be safely represented. Beyond this range, precision is lost:

javascript
// Beyond this range, precision is lost
console.log(9007199254740992 === 9007199254740993);  // true! (wrong!)

For larger integers, use BigInt.


BigInt

BigInt (ES2020) represents integers larger than Number.MAX_SAFE_INTEGER.

javascript
// Add 'n' suffix to create a BigInt
let big = 9007199254740993n;
let alsoBig = BigInt("9007199254740993");

console.log(big + 1n);  // 9007199254740994n (correct!)

BigInt Rules

javascript
// Cannot mix BigInt and Number
let big = 10n;
let regular = 5;
// console.log(big + regular);  // TypeError!

// Must convert explicitly
console.log(big + BigInt(regular));  // 15n
console.log(Number(big) + regular);  // 15
<Note> **When to use BigInt:** Cryptography, precise timestamps, database IDs, any calculation requiring integers larger than 9 quadrillion. </Note>

Boolean

Boolean has exactly two values: true and false.

javascript
let isLoggedIn = true;
let hasPermission = false;

// From comparisons
let isAdult = age >= 18;        // true or false
let isEqual = name === "Alice"; // true or false

Truthy and Falsy

When used in boolean contexts (like if statements), all values are either "truthy" or "falsy":

javascript
// Falsy values (only 8!)
false
0
-0
0n        // BigInt zero
""        // Empty string
null
undefined
NaN

// Everything else is truthy
"hello"   // truthy
42        // truthy
[]        // truthy (empty array!)
{}        // truthy (empty object!)
javascript
// Convert any value to boolean
let value = "hello";
let bool = Boolean(value);  // true
let shortcut = !!value;     // true (double negation trick)
<Tip> Learn more about how JavaScript converts between types in the [Type Coercion](/concepts/type-coercion) section. </Tip>

undefined

undefined means "no value has been assigned." JavaScript uses it automatically in several situations:

javascript
// 1. Declared but not assigned
let x;
console.log(x);  // undefined

// 2. Missing function parameters
function greet(name) {
  console.log(name);  // undefined if called without argument
}
greet();

// 3. Function with no return statement
function doNothing() {
  // no return
}
console.log(doNothing());  // undefined

// 4. Accessing non-existent object property
let person = { name: "Alice" };
console.log(person.age);  // undefined
<Tip> Don't explicitly assign `undefined` to variables. Use `null` instead to indicate "intentionally empty." </Tip>

null

null means "intentionally empty". You're explicitly saying "this has no value."

javascript
// Intentionally clearing a variable
let user = { name: "Alice" };
user = null;  // User logged out, clearing the reference

// Indicating no result
function findUser(id) {
  // ... search logic ...
  return null;  // User not found
}

The Famous typeof Bug

javascript
console.log(typeof null);  // "object" — Wait, what?!

Yes, really. This is one of JavaScript's most famous quirks! It's a historical mistake from JavaScript's first implementation in 1995. It was never fixed because too much existing code depends on it.

javascript
// How to properly check for null
let value = null;
console.log(value === null);  // true (use strict equality)

Symbol

Symbol (ES6) creates unique identifiers. According to MDN, Symbol was the first new primitive type added to JavaScript since its creation in 1995. Even symbols with the same description are different.

javascript
let id1 = Symbol("id");
let id2 = Symbol("id");

console.log(id1 === id2);  // false — always unique!
console.log(id1.description);  // "id" (the description)

Use Case: Unique Object Keys

javascript
const ID = Symbol("id");
const user = {
  name: "Alice",
  [ID]: 12345  // Symbol as property key
};

console.log(user.name);    // "Alice"
console.log(user[ID]);     // 12345

// Symbol keys don't appear in normal iteration
console.log(Object.keys(user));  // ["name"] — ID not included

Well-Known Symbols

JavaScript has built-in symbols for customizing object behavior:

javascript
// Symbol.iterator - make an object iterable
// Symbol.toStringTag - customize Object.prototype.toString
// Symbol.toPrimitive - customize type conversion

These are called well-known symbols and allow you to customize how objects behave with built-in operations.

<Note> Symbols are an advanced feature. As a beginner, focus on understanding that they exist and create unique values. You'll encounter them when diving into advanced patterns and library code. </Note>

The typeof Operator

The typeof operator returns a string indicating the type of a value.

javascript
console.log(typeof "hello");     // "string"
console.log(typeof 42);          // "number"
console.log(typeof 42n);         // "bigint"
console.log(typeof true);        // "boolean"
console.log(typeof undefined);   // "undefined"
console.log(typeof Symbol());    // "symbol"
console.log(typeof null);        // "object" ⚠️ (bug!)
console.log(typeof {});          // "object"
console.log(typeof []);          // "object"
console.log(typeof function(){}); // "function"

typeof Results Table

Valuetypeof ResultNotes
"hello""string"
42"number"
42n"bigint"
true / false"boolean"
undefined"undefined"
Symbol()"symbol"
null"object"Historical bug!
{}"object"
[]"object"Arrays are objects
function(){}"function"Functions are special

Better Type Checking

Since typeof has quirks, here are more reliable alternatives:

javascript
// Check for null specifically
let value = null;
if (value === null) {
  console.log("It's null");
}

// Check for arrays
Array.isArray([1, 2, 3]);  // true
Array.isArray("hello");    // false

// Get precise type with Object.prototype.toString
Object.prototype.toString.call(null);       // "[object Null]"
Object.prototype.toString.call([]);         // "[object Array]"
Object.prototype.toString.call(new Date()); // "[object Date]"

Array.isArray() is the reliable way to check for arrays, since typeof [] returns "object". For more complex type checking, Object.prototype.toString() gives precise type information.


Immutability Explained

Immutable means "cannot be changed." Primitive values are immutable. You cannot alter the value itself.

Seeing Immutability in Action

javascript
let str = "hello";

// These methods don't change 'str' — they return NEW strings
str.toUpperCase();     // Returns "HELLO"
console.log(str);      // Still "hello"!

// To capture the new value, you must reassign
str = str.toUpperCase();
console.log(str);      // Now "HELLO"

Visual: What Happens in Memory

BEFORE str.toUpperCase():
┌─────────────────┐
│  str → "hello"  │   (original string)
└─────────────────┘

AFTER str.toUpperCase() (without reassignment):
┌─────────────────┐
│  str → "hello"  │   (unchanged!)
└─────────────────┘
┌─────────────────┐
│     "HELLO"     │   (new string, not captured, garbage collected)
└─────────────────┘

AFTER str = str.toUpperCase():
┌─────────────────┐
│  str → "HELLO"  │   (str now points to new string)
└─────────────────┘

Common Misconception: const vs Immutability

const prevents reassignment, not mutation. These are different concepts!

javascript
// const prevents reassignment
const name = "Alice";
// name = "Bob";  // Error! Cannot reassign const

// But const doesn't make objects immutable
const person = { name: "Alice" };
person.name = "Bob";     // Works! Mutating the object
person.age = 25;         // Works! Adding a property
// person = {};          // Error! Cannot reassign const

// Primitives are immutable regardless of const/let
let str = "hello";
str[0] = "H";  // Silently fails — can't mutate primitive
<Tip> Think of it this way: `const` protects the **variable** (the container). Immutability protects the **value** (the content). </Tip>

Autoboxing: The Secret Life of Primitives

If primitives have no methods, how does "hello".toUpperCase() work?

The Magic Behind the Scenes

When you access a property or method on a primitive, JavaScript temporarily wraps it in an object:

<Steps> <Step title="You Call a Method on a Primitive"> ```javascript "hello".toUpperCase() ``` </Step> <Step title="JavaScript Creates a Wrapper Object"> Behind the scenes, JavaScript does something like: ```javascript (new String("hello")).toUpperCase() ``` </Step> <Step title="Method Executes and Returns"> The `toUpperCase()` method runs and returns `"HELLO"`. </Step> <Step title="Wrapper Object Is Discarded"> The temporary `String` object is thrown away. The original primitive `"hello"` is unchanged. </Step> </Steps>

Wrapper Objects

Each primitive type (except null and undefined) has a corresponding wrapper object:

PrimitiveWrapper Object
stringString
numberNumber
booleanBoolean
bigintBigInt
symbolSymbol

Don't Use new String() etc.

You can create wrapper objects manually, but don't:

javascript
// Don't do this!
let strObj = new String("hello");
console.log(typeof strObj);        // "object" (not "string"!)
console.log(strObj === "hello");   // false (object vs primitive)

// Do this instead
let str = "hello";
console.log(typeof str);           // "string"
<Warning> Using `new String()`, `new Number()`, or `new Boolean()` creates **objects**, not primitives. This can cause confusing bugs with equality checks and typeof. </Warning>

null vs undefined

These two "empty" values confuse many developers. Here's how they differ:

<Tabs> <Tab title="Side-by-Side Comparison"> | Aspect | `undefined` | `null` | |--------|-------------|--------| | **Meaning** | "No value assigned yet" | "Intentionally empty" | | **Set by** | JavaScript automatically | Developer explicitly | | **typeof** | `"undefined"` | `"object"` (bug) | | **In JSON** | Omitted from output | Preserved as `null` | | **Default params** | Triggers default | Doesn't trigger default | | **Loose equality** | `null == undefined` is `true` | | | **Strict equality** | `null === undefined` is `false` | | </Tab> <Tab title="When JavaScript Uses undefined"> ```javascript // 1. Uninitialized variables let x; console.log(x); // undefined
// 2. Missing function arguments
function greet(name) {
  console.log(name);
}
greet();  // undefined

// 3. No return statement
function noReturn() {}
console.log(noReturn());  // undefined

// 4. Non-existent properties
let obj = {};
console.log(obj.missing);  // undefined

// 5. Array holes
let arr = [1, , 3];
console.log(arr[1]);  // undefined
```
</Tab> <Tab title="When to Use null"> ```javascript // 1. Explicitly "clearing" a value let user = { name: "Alice" }; user = null; // User logged out
// 2. Function returning "no result"
function findUser(id) {
  // Search logic...
  return null;  // Not found
}

// 3. Optional object properties
let config = {
  cache: true,
  timeout: null  // Explicitly no timeout
};

// 4. Resetting references
let timer = setTimeout(callback, 1000);
clearTimeout(timer);
timer = null;  // Clear reference
```
</Tab> </Tabs>

Best Practices

javascript
// Check for either null or undefined (loose equality)
if (value == null) {
  console.log("Value is null or undefined");
}

// Check for specifically undefined
if (value === undefined) {
  console.log("Value is undefined");
}

// Check for specifically null
if (value === null) {
  console.log("Value is null");
}

// Check for "has a value" (not null/undefined)
if (value != null) {
  console.log("Value exists");
}

The #1 Primitive Mistake: Using Wrapper Constructors

The most common mistake developers make with primitives is using new String(), new Number(), or new Boolean() instead of literal values.

┌─────────────────────────────────────────────────────────────────────────┐
│                     PRIMITIVES VS WRAPPER OBJECTS                        │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  WRONG WAY                              RIGHT WAY                        │
│  ─────────                              ─────────                        │
│  new String("hello")                    "hello"                          │
│  new Number(42)                         42                               │
│  new Boolean(true)                      true                             │
│                                                                          │
│  typeof new String("hi") → "object"     typeof "hi" → "string"          │
│  new String("hi") === "hi" → false      "hi" === "hi" → true            │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
javascript
// ❌ WRONG - Creates an object, not a primitive
const str = new String("hello");
console.log(typeof str);        // "object" (not "string"!)
console.log(str === "hello");   // false (object vs primitive)

// ✓ CORRECT - Use primitive literals
const str2 = "hello";
console.log(typeof str2);       // "string"
console.log(str2 === "hello");  // true
<Warning> **The Trap:** Using `new String()`, `new Number()`, or `new Boolean()` creates wrapper **objects**, not primitives. This breaks equality checks (`===`), `typeof` comparisons, and can cause subtle bugs. Always use literal syntax: `"hello"`, `42`, `true`. </Warning> <Tip> **Rule of Thumb:** Never use `new` with `String`, `Number`, or `Boolean`. The only exception is when you intentionally need the wrapper object (which is rare). For type conversion, use them as functions without `new`: `String(123)` returns `"123"` (a primitive). </Tip>

JavaScript Quirks & Gotchas

JavaScript has some famous "weird parts" that every developer should know. Most relate to primitives and type coercion.

<AccordionGroup> <Accordion title="1. typeof null === 'object'"> ```javascript console.log(typeof null); // "object" ```
**Why?** This is a bug from JavaScript's first implementation in 1995. In the original code, values had a small label to identify their type. Objects had the label `000`, and `null` was represented as the NULL pointer (`0x00`), which also had `000`.

**Why not fixed?** A proposal to fix it was rejected because too much existing code checks `typeof x === "object"` and expects `null` to pass.

**Workaround:**
```javascript
// Always check for null explicitly
if (value !== null && typeof value === "object") {
  // It's a real object
}
```
</Accordion> <Accordion title="2. NaN !== NaN"> ```javascript console.log(NaN === NaN); // false! console.log(NaN !== NaN); // true! ```
NaN is so confused about its identity that it doesn't even equal itself!

**Why?** By the IEEE 754 specification, NaN represents "Not a Number", an undefined or unrepresentable result. Since it's not a specific number, it can't equal anything, including itself.

**How to check for NaN:**
```javascript
// Don't do this
if (value === NaN) { }  // Never true!

// Do this instead
if (Number.isNaN(value)) { }  // ES6, recommended
if (isNaN(value)) { }         // Older, has quirks
```

<Note>
[`isNaN()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN) converts the value first, so `isNaN("hello")` is `true`. [`Number.isNaN()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) only returns `true` for actual `NaN`.
</Note>
</Accordion> <Accordion title="3. 0.1 + 0.2 !== 0.3"> ```javascript console.log(0.1 + 0.2); // 0.30000000000000004 console.log(0.1 + 0.2 === 0.3); // false ```
**Why?** Computers store numbers in binary. Just like 1/3 can't be perfectly represented in decimal (0.333...), 0.1 can't be perfectly represented in binary.

**Solutions:**
```javascript
// 1. Work in integers (cents, not dollars) — RECOMMENDED
let totalCents = 10 + 20;  // 30 (accurate!)
let dollars = totalCents / 100;  // 0.3

// 2. Use Intl.NumberFormat for display
new Intl.NumberFormat('en-US', { 
  style: 'currency', 
  currency: 'USD' 
}).format(0.30);  // "$0.30"

// 3. Compare with tolerance for equality checks
Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON;  // true (Number.EPSILON is the smallest difference)

// 4. Use toFixed() for simple rounding
(0.1 + 0.2).toFixed(2);  // "0.30"
```
</Accordion> <Accordion title="4. Empty String is Falsy, But..."> ```javascript console.log(Boolean("")); // false (empty string is falsy) console.log(Boolean(" ")); // true (space is truthy!) console.log(Boolean("0")); // true (string "0" is truthy!) console.log(Boolean(0)); // false (number 0 is falsy)
console.log("" == false);      // true (coercion)
console.log("" === false);     // false (different types)
```

**The lesson:** Be careful with truthy/falsy checks on strings. An empty string `""` is falsy, but a string with just whitespace `" "` is truthy.

```javascript
// Check for empty or whitespace-only string
if (str.trim() === "") {
  console.log("String is empty or whitespace");
}
```
</Accordion> <Accordion title="5. + Operator String Concatenation"> ```javascript console.log(1 + 2); // 3 (number addition) console.log("1" + "2"); // "12" (string concatenation) console.log(1 + "2"); // "12" (number converted to string!) console.log("1" + 2); // "12" (number converted to string!) console.log(1 + 2 + "3"); // "33" (left to right: 1+2=3, then 3+"3"="33") console.log("1" + 2 + 3); // "123" (left to right: "1"+2="12", "12"+3="123") ```
**Why?** The `+` operator does addition for numbers, but concatenation for strings. When mixed, JavaScript converts numbers to strings.

**Be explicit:**
```javascript
// Force number addition
Number("1") + Number("2");  // 3
parseInt("1") + parseInt("2");  // 3

// Force string concatenation
String(1) + String(2);  // "12"
`${1}${2}`;  // "12"
```
</Accordion> </AccordionGroup> <Tip> **Want to go deeper?** Kyle Simpson's book "You Don't Know JS: Types & Grammar" is the definitive guide to understanding these quirks. It's free to read online! </Tip>

Key Takeaways

<Info> **The key things to remember about Primitive Types:**
  1. 7 primitives: string, number, bigint, boolean, undefined, null, symbol

  2. Primitives are immutable — you can't change the value itself, only create new values

  3. Compared by value"hello" === "hello" is true because the values match

  4. typeof works for most types — except typeof null returns "object" (historical bug)

  5. Autoboxing allows primitives to use methods — JavaScript wraps them temporarily

  6. undefined vs null — undefined is "not assigned," null is "intentionally empty"

  7. Be aware of gotchasNaN !== NaN, 0.1 + 0.2 !== 0.3, falsy values

  8. Don't use new String() etc. — creates objects, not primitives

  9. Symbols create unique identifiers — even Symbol("id") !== Symbol("id")

  10. Use Number.isNaN() to check for NaN — don't use equality comparison since NaN !== NaN

    </Info>

Test Your Knowledge

<AccordionGroup> <Accordion title="Question 1: What are the 7 primitive types in JavaScript?"> **Answer:** 1. `string` - text data 2. `number` - integers and decimals 3. `bigint` - large integers 4. `boolean` - true/false 5. `undefined` - no value assigned 6. `null` - intentionally empty 7. `symbol` - unique identifier
Remember: Everything else is an object (arrays, functions, dates, etc.).
</Accordion> <Accordion title="Question 2: What does typeof null return and why?"> **Answer:** `typeof null` returns `"object"`.
This is a bug from JavaScript's original implementation in 1995. Values were stored with type tags, and both objects and `null` had the same `000` tag. The bug was never fixed because too much existing code depends on this behavior.

To check for null, use `value === null` instead.
</Accordion> <Accordion title="Question 3: Why does 0.1 + 0.2 !== 0.3?"> **Answer:** Because JavaScript (like all languages) uses binary floating-point (IEEE 754) to store numbers.
Just like 1/3 can't be perfectly represented in decimal (0.333...), 0.1 can't be perfectly represented in binary. The tiny rounding errors accumulate, giving us `0.30000000000000004` instead of `0.3`.

Solutions: Use integers (work in cents), use `toFixed()` for display, compare with tolerance, or use a decimal math library.
</Accordion> <Accordion title="Question 4: What's the difference between null and undefined?"> **Answer:** - **`undefined`**: Means "no value has been assigned." JavaScript sets this automatically for uninitialized variables, missing function arguments, and non-existent properties.
- **`null`**: Means "intentionally empty." Developers use this explicitly to indicate "this has no value on purpose."

Key difference: `undefined` is the *default* empty value; `null` is the *intentional* empty value.

```javascript
let x;           // undefined (automatic)
let y = null;    // null (explicit)
```
</Accordion> <Accordion title="Question 5: How can 'hello'.toUpperCase() work if primitives have no methods?"> **Answer:** Through **autoboxing** (also called "auto-wrapping").
When you call a method on a primitive:
1. JavaScript temporarily wraps it in a wrapper object (`String`, `Number`, etc.)
2. The method is called on the wrapper object
3. The result is returned
4. The wrapper object is discarded

So `"hello".toUpperCase()` becomes `(new String("hello")).toUpperCase()` behind the scenes. The original primitive `"hello"` is never changed.
</Accordion> <Accordion title="Question 6: Why can't you use === to check if a value is NaN?"> **Answer:** Because `NaN` is the only value in JavaScript that is not equal to itself!
```javascript
console.log(NaN === NaN);  // false!
```

This is per the IEEE 754 floating-point specification. `NaN` represents an undefined or unrepresentable mathematical result, so it can't equal anything, including itself.

**How to check for NaN:**
```javascript
// ❌ WRONG - Never works!
if (value === NaN) { }

// ✓ CORRECT - Use Number.isNaN()
if (Number.isNaN(value)) { }
```
</Accordion> </AccordionGroup>

Frequently Asked Questions

<AccordionGroup> <Accordion title="What are the 7 primitive types in JavaScript?"> JavaScript has exactly seven primitive types: string, number, bigint, boolean, undefined, null, and symbol. As defined by the ECMAScript specification, these are the fundamental building blocks of all data in JavaScript — everything else is an object. </Accordion> <Accordion title="Why does typeof null return 'object' in JavaScript?"> This is a well-documented bug from JavaScript's first implementation in 1995. In the original C source code, values were stored with type tags, and both objects and null shared the `000` tag. A proposal to fix it (typeof null === "null") was rejected by TC39 because too much existing code depends on the current behavior. </Accordion> <Accordion title="What is autoboxing in JavaScript?"> Autoboxing is the process where JavaScript temporarily wraps a primitive in its corresponding wrapper object (String, Number, Boolean) when you access a property or call a method. For example, `"hello".toUpperCase()` works because the engine briefly creates a String object, calls the method, and discards the wrapper. As documented in MDN, this is why primitives appear to have methods even though they are not objects. </Accordion> <Accordion title="What is the difference between null and undefined in JavaScript?"> `undefined` means "no value has been assigned" and is set automatically by the JavaScript engine. `null` means "intentionally empty" and is always set explicitly by the developer. According to the ECMAScript specification, `null == undefined` is true under loose equality, but `null === undefined` is false because they are different types. </Accordion> <Accordion title="Why does 0.1 + 0.2 not equal 0.3 in JavaScript?"> This happens because JavaScript uses the IEEE 754 double-precision floating-point standard to represent numbers. Certain decimal fractions like 0.1 cannot be represented exactly in binary, leading to tiny rounding errors. This is not a JavaScript-specific issue — the same behavior occurs in Python, Java, C++, and virtually every language that uses IEEE 754. </Accordion> </AccordionGroup>
<CardGroup cols={2}> <Card title="Primitives vs Objects" icon="clone" href="/concepts/primitives-objects"> How primitives and objects behave differently in JavaScript </Card> <Card title="Type Coercion" icon="shuffle" href="/concepts/type-coercion"> How JavaScript converts between types automatically </Card> <Card title="== vs === vs typeof" icon="equals" href="/concepts/equality-operators"> Understanding equality operators and type checking </Card> <Card title="Scope & Closures" icon="layer-group" href="/concepts/scope-and-closures"> How variables are accessed and how functions remember their environment </Card> </CardGroup>

Reference

<CardGroup cols={2}> <Card title="Primitive — MDN Glossary" icon="book" href="https://developer.mozilla.org/en-US/docs/Glossary/Primitive"> Official MDN glossary definition of primitive values in JavaScript. </Card> <Card title="JavaScript data types and data structures — MDN" icon="book" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures"> Comprehensive MDN guide to JavaScript's type system and data structures. </Card> <Card title="typeof operator — MDN" icon="book" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof"> Complete reference for the typeof operator including its quirks and return values. </Card> <Card title="Symbol — MDN" icon="book" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol"> Deep dive into JavaScript Symbols, well-known symbols, and use cases. </Card> </CardGroup>

Articles

<CardGroup cols={2}> <Card title="Primitive and Non-primitive data-types in JavaScript" icon="newspaper" href="https://www.geeksforgeeks.org/primitive-and-non-primitive-data-types-in-javascript"> Beginner-friendly overview covering all 7 primitive types with a comparison table showing the differences between primitives and objects. </Card> <Card title="How numbers are encoded in JavaScript" icon="newspaper" href="http://2ality.com/2012/04/number-encoding.html"> Expert deep-dive by Dr. Axel Rauschmayer into IEEE 754 floating-point representation, explaining why 0.1 + 0.2 !== 0.3 and how JavaScript stores numbers internally. </Card> <Card title="(Not) Everything in JavaScript is an Object" icon="newspaper" href="https://dev.to/d4nyll/not-everything-in-javascript-is-an-object"> Debunks the common myth that "everything in JS is an object." Shows how autoboxing creates the illusion that primitives have methods, with diagrams explaining what happens behind the scenes. </Card> <Card title="Methods of Primitives" icon="newspaper" href="https://javascript.info/primitives-methods"> The javascript.info guide walks through each wrapper type (String, Number, Boolean) and includes interactive tasks to test your understanding. One of the best resources for learning autoboxing. </Card> <Card title="The Differences Between Object.freeze() vs Const" icon="newspaper" href="https://medium.com/@bolajiayodeji/the-differences-between-object-freeze-vs-const-in-javascript-4eacea534d7c"> Clears up the common confusion between const (prevents reassignment) and immutability (prevents mutation). Short and beginner-friendly. </Card> </CardGroup>

Books

<Card title="You Don't Know JS: Types & Grammar — Kyle Simpson" icon="book" href="https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/types-grammar/README.md"> The definitive deep-dive into JavaScript types. Free to read online. Covers primitives, coercion, and the "weird parts" that trip up developers. Essential reading for understanding JavaScript's type system. </Card>

Courses

<CardGroup cols={2}> <Card title="JavaScript: Understanding the Weird Parts (First 3.5 Hours) — Anthony Alicea" icon="graduation-cap" href="https://www.youtube.com/watch?v=Bv_5Zv5c-Ts"> Free preview of one of the most acclaimed JavaScript courses ever made. Covers types, coercion, and the "weird parts" that confuse developers. Perfect starting point before buying the full course. </Card> <Card title="JavaScript: Understanding the Weird Parts (Full Course) — Anthony Alicea" icon="graduation-cap" href="https://www.udemy.com/course/understand-javascript/"> The complete 12-hour course covering types, operators, objects, and engine internals. Anthony's explanations of scope, closures, and prototypes are particularly helpful for intermediate developers. </Card> <Card title="Introduction to Primitives — Piccalilli" icon="graduation-cap" href="https://piccalil.li/javascript-for-everyone/lessons/9"> Part of the "JavaScript for Everyone" course by Mat Marquis. This module covers all 7 primitive types with dedicated lessons for Numbers, Strings, Booleans, null/undefined, BigInt, and Symbol. Beautifully written with a fun narrative style. </Card> </CardGroup>

Videos

<CardGroup cols={2}> <Card title="JavaScript Reference vs Primitive Types — Academind" icon="video" href="https://www.youtube.com/watch?v=9ooYYRLdg_g"> Academind's Max shows what happens in memory when you copy primitives vs objects. The side-by-side code examples make the difference immediately obvious. </Card> <Card title="Value Types and Reference Types — Programming with Mosh" icon="video" href="https://www.youtube.com/watch?v=e-_mDyqm2oU"> Mosh Hamedani's clear teaching style makes this complex topic easy to understand. Includes practical examples showing memory behavior. </Card> <Card title="Everything You Never Wanted to Know About JavaScript Numbers — JSConf" icon="video" href="https://www.youtube.com/watch?v=MqHDDtVYJRI"> JSConf talk by Bartek Szopka diving deep into the quirks of JavaScript numbers. Covers IEEE 754, precision issues, and edge cases. </Card> </CardGroup>