Ad – 728Γ—90
🌱 Beginner

JavaScript Data Types – Primitives and Reference Types

Every value in JavaScript has a type β€” knowing that type determines what you can do with it. JavaScript has seven primitive types and a family of reference types, and understanding the difference between them is one of the most important foundations in the language. This lesson covers all seven primitives, how reference types work in memory, the typeof operator, and the subtle distinction between passing values versus passing references.

⏱️ 24 min read 🎯 Beginner πŸ“… Updated 2026

Primitives vs Reference Types

JavaScript values fall into two categories:

  • Primitive types β€” simple, immutable values stored directly in memory. There are exactly 7.
  • Reference types β€” complex values (objects, arrays, functions) stored as a reference (pointer) to a memory location.
CategoryTypesExample
Primitivestring"hello"
Primitivenumber42, 3.14
Primitivebigint9007199254740993n
Primitivebooleantrue, false
Primitiveundefinedundefined
Primitivenullnull
PrimitivesymbolSymbol("id")
Referenceobject{ name: "Alice" }
Referencearray[1, 2, 3]
Referencefunctionfunction() {}

The 7 Primitive Types

String

A string is a sequence of characters. Strings are immutable β€” you can't change individual characters in place; methods return new strings.

JavaScript
let greeting = "Hello, World!";
let name = 'Alice';
let template = `My name is ${name}`;

console.log(typeof greeting);   // "string"
console.log(greeting.length);   // 13
console.log(greeting[0]);       // "H"
β–Ά Output
string 13 H

Number

JavaScript uses a single number type for both integers and floating-point numbers (64-bit IEEE 754). This includes NaN (Not a Number) and Infinity.

JavaScript
let integer = 42;
let float   = 3.14;
let negative = -100;

console.log(typeof integer);     // "number"
console.log(typeof NaN);         // "number" (surprising!)
console.log(typeof Infinity);    // "number"

console.log(1 / 0);              // Infinity
console.log(-1 / 0);             // -Infinity
console.log(0 / 0);              // NaN
console.log(NaN === NaN);        // false β€” NaN is not equal to itself!
β–Ά Output
number number number Infinity -Infinity NaN false

Boolean, BigInt, undefined, null, Symbol

JavaScript
// Boolean
let isActive = true;
let isAdmin  = false;
console.log(typeof isActive);      // "boolean"

// BigInt β€” for integers beyond Number.MAX_SAFE_INTEGER
let bigNum = 9007199254740993n;
console.log(typeof bigNum);        // "bigint"

// undefined β€” variable declared but not assigned
let notAssigned;
console.log(notAssigned);          // undefined
console.log(typeof notAssigned);   // "undefined"

// null β€” intentional absence of a value
let empty = null;
console.log(empty);                // null
console.log(typeof null);          // "object" ← famous JS bug!

// Symbol β€” unique, immutable identifier
let id1 = Symbol("id");
let id2 = Symbol("id");
console.log(id1 === id2);          // false β€” every Symbol is unique
β–Ά Output
boolean bigint undefined undefined null object false
⚠️
typeof null === "object" is a Historical Bug

This is one of JavaScript's most notorious quirks β€” typeof null returns "object", not "null". It has been this way since JavaScript was created in 1995 and fixing it would break too much existing code. To check for null, use === null directly.

Ad – 336Γ—280

Reference Types – Objects, Arrays, Functions

Reference types are stored as references (memory addresses). Variables don't hold the value itself β€” they hold a pointer to where the value lives in the heap.

JavaScript
// Object
const person = { name: "Alice", age: 30 };
console.log(typeof person);     // "object"

// Array (also type "object"!)
const colors = ["red", "green", "blue"];
console.log(typeof colors);     // "object"
console.log(Array.isArray(colors));  // true β€” use this to check for arrays

// Function
function greet() { return "Hello!"; }
console.log(typeof greet);      // "function" (special case of object)

null vs undefined

Both represent "no value" but they have different semantic meanings:

  • undefined β€” the variable exists but has not been assigned a value. JavaScript sets this automatically.
  • null β€” the programmer intentionally set the variable to "empty" or "no object". It's an explicit assignment.
JavaScript
let a;             // JavaScript sets this to undefined
let b = null;      // Developer explicitly says "no value"

console.log(a);           // undefined
console.log(b);           // null
console.log(a == b);      // true  β€” loose equality coerces them
console.log(a === b);     // false β€” strict equality: different types
β–Ά Output
undefined null true false

Dynamic Typing

JavaScript is dynamically typed β€” a variable can hold a value of any type, and you can change the type by simply assigning a new value. This is flexible but requires care.

JavaScript
let value = 42;
console.log(typeof value);   // "number"

value = "hello";
console.log(typeof value);   // "string"

value = true;
console.log(typeof value);   // "boolean"

value = { x: 1 };
console.log(typeof value);   // "object"

Pass by Value vs Pass by Reference

This is one of the most important concepts to understand for avoiding bugs:

JavaScript
// Primitives are passed BY VALUE β€” a copy is made
let x = 10;
let y = x;       // y gets a copy of 10
y = 20;
console.log(x);  // 10 β€” x is unchanged

// Objects are passed BY REFERENCE β€” both point to the same object
let obj1 = { name: "Alice" };
let obj2 = obj1;       // obj2 points to the SAME object
obj2.name = "Bob";
console.log(obj1.name); // "Bob" β€” obj1 was also changed!

// To copy an object, use spread or Object.assign
let obj3 = { ...obj1 };  // shallow copy
obj3.name = "Charlie";
console.log(obj1.name);  // "Bob" β€” obj1 unchanged this time
β–Ά Output
10 Bob Bob
πŸ’‘
Immutability of Primitives

Primitive values are always immutable β€” you cannot change a string's characters in place, and number operations always produce a new number. When you write let s = "hello"; s = s.toUpperCase();, you're not modifying the original string β€” you're creating a new one and reassigning the variable.

πŸ‹οΈ Practical Exercise

  1. Use typeof on a string, number, boolean, undefined, null, object, array, and function. Note which ones surprise you.
  2. Demonstrate that NaN === NaN is false. Then use Number.isNaN() to properly check for NaN.
  3. Create two variables pointing to the same object. Mutate one property through the second variable and observe that the first variable's object also changed.
  4. Create a shallow copy of an object using the spread operator { ...obj } and verify that changes to the copy don't affect the original.
  5. Check the difference between null == undefined and null === undefined.

πŸ”₯ Challenge Exercise

Write a function describeType(value) that takes any value and returns a human-readable description: e.g., "a string of 5 characters", "the number 42", "an array of 3 elements", "null (intentional absence)", "undefined (not assigned)", or "an object with 2 properties". Use typeof, Array.isArray(), and other checks to handle all the cases.

πŸ“‹ Summary

  • JavaScript has 7 primitive types: string, number, bigint, boolean, undefined, null, symbol.
  • Reference types (object, array, function) store a pointer to a heap location, not the value itself.
  • typeof null === "object" is a historical bug β€” check for null with === null.
  • undefined means unassigned; null means intentionally empty.
  • Primitives are copied by value; objects/arrays are copied by reference.
  • Use Array.isArray() to check for arrays since typeof [] returns "object".

Interview Questions

  • What are the 7 primitive types in JavaScript?
  • Why does typeof null return "object"?
  • What is the difference between null and undefined?
  • How do you check if a variable is an array?
  • What is the difference between pass by value and pass by reference?
  • Why is NaN === NaN equal to false?

Frequently Asked Questions

What is the difference between == and === for null and undefined? +

null == undefined is true β€” loose equality considers them equal. null === undefined is false β€” strict equality requires the same type and value. A common pattern is if (value == null) to catch both null and undefined in one check.

Is JavaScript's dynamic typing a weakness? +

It's a trade-off. Dynamic typing makes JavaScript quick to write and flexible for small scripts. However, in large codebases it can lead to type-related bugs. That's why TypeScript was created β€” it adds a static type layer on top of JavaScript. Most large JavaScript projects today use TypeScript for safety while still compiling to regular JS.

When would I use Symbol? +

Symbols are mainly used for creating unique property keys that won't accidentally conflict with other code. They're commonly used in library code to avoid name collisions β€” for example, defining a unique "hidden" property on an object without overwriting an existing property with the same name. As a beginner, you'll rarely need to create Symbols directly.

What is the safe integer limit in JavaScript? +

The safe integer range is Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER, which is Β±(253 – 1) or about Β±9 quadrillion. Beyond this, floating-point representation causes precision errors. Use BigInt when you need exact arithmetic with very large integers (cryptocurrency amounts, database IDs, etc.).

How do I make a deep copy of an object? +

The spread operator { ...obj } creates a shallow copy β€” nested objects are still shared by reference. For a deep copy, use structuredClone(obj) (modern browsers and Node 17+), or the older JSON.parse(JSON.stringify(obj)) trick (which loses functions, undefined, Dates, etc.). structuredClone is the recommended modern approach.