Skip to main content

Data types in JavaScript

JavaScript data types split into two groups: seven primitives (immutable, copied by value) and objects (mutable, copied by reference).

Theory

TL;DR

  • Primitives are like printed postcards: copy one and you get an independent duplicate.
  • Objects are like a shared Google Doc: all "copies" point to the same underlying data.
  • Seven primitives: undefined, null, boolean, number, string, symbol, bigint.
  • Objects include plain objects, arrays, functions, dates, regular expressions, and more.
  • typeof null returns "object", not "null". A known bug from JavaScript's first version.

Quick Example

javascript
// Primitives: assignment copies the value let a = 5; let b = a; // b gets its own copy of 5 b = 10; console.log(a); // 5, unchanged // Objects: assignment copies the reference let x = { val: 5 }; let y = x; // y points to the same object y.val = 10; console.log(x.val); // 10, x changed too

These two snippets explain most of the bugs developers file on this topic.

Key Difference

When you assign a primitive, JavaScript copies the actual value into a new slot in memory. The two variables become independent. Objects work differently: the variable stores a reference (a memory address), not the data itself. Assign that variable to another and both point at the same heap object. Mutate one, and the change shows up through both.

When to Use

  • Loop counter → number primitive.
  • User's display name → string primitive.
  • User profile with multiple fields → object.
  • Toggle flag in React state → boolean primitive.
  • Form data in React state → object.
  • Integers beyond 2^53 - 1 → bigint.

Memory Model

V8 stores primitives on the stack for fast, predictable access. Objects live on the heap, and only the pointer lives on the stack. That pointer is what gets copied during assignment, which is why two variables can unknowingly share the same heap data. Object.create and similar APIs manipulate those heap structures directly.

Common Mistakes

Mistake 1: treating arrays like primitives

javascript
let arr1 = [1, 2]; let arr2 = arr1; // reference copy, not a new array arr2.push(3); console.log(arr1); // [1, 2, 3], not what you expected

Arrays are objects. Fix: let arr2 = [...arr1] or arr1.slice().

Mistake 2: trying to mutate a string character by character

javascript
let s = "hello"; s[0] = "H"; // no error, but nothing changes console.log(s); // "hello"

Strings are immutable. To change: s = "H" + s.slice(1).

Mistake 3: comparing objects with ===

javascript
console.log({} === {}); // false, different references console.log([1] === [1]); // false

Objects compare by reference, not content. For structural comparison use JSON.stringify or lodash.isEqual. For more on how == behaves with different types, see type coercion in JavaScript.

Mistake 4: typeof null

javascript
typeof null === "object" // true typeof null === "null" // false

This is a bug from Netscape's JavaScript 1.0. The internal type tag for null collided with the object type tag. Proposals to fix it were rejected over backward compatibility. Check for null explicitly: value === null.

Mistake 5: BigInt in JSON

javascript
const id = 123n; JSON.stringify(id); // TypeError

JSON has no BigInt support. Convert before serializing: id.toString(), or pass a replacer function to JSON.stringify.

Real-world Usage

  • React: props as objects { userId: 123, name: "Alice" }; list keys as primitives key={id}.
  • Express: req.body is an object parsed from JSON; res.status(200) takes a number primitive.
  • Node.js fs: the err parameter in callbacks is either null (primitive) or an Error object.
  • Redux: state is an object tree; action type is a string primitive like "INCREMENT".
  • Lodash: _.cloneDeep(obj) deep-copies nested objects without sharing any references.

Follow-up Questions

Q: What happens if you pass a primitive to a function and modify it inside?
A: The function gets a copy. Changes inside don't affect the original. With objects the function gets the reference, so mutations are visible to the caller.

Q: Why does typeof null return "object"?
A: It's a bug from JavaScript's first release at Netscape. The original type tag for null matched the object type tag. Multiple proposals to fix it failed because the change would break existing websites.

Q: Name all seven primitives.
A: undefined, null, boolean, number, string, symbol (ES2015), bigint (ES2020).

Q: How is BigInt different from Number?
A: Number is a 64-bit float with a safe integer limit of 2^53 - 1. BigInt handles integers of arbitrary size. They don't coerce into each other implicitly, so 1n + 1 throws a TypeError.

Q (senior): An object is passed to an async callback. The caller mutates the object before the callback runs. What does the callback see?
A: The mutated version. The callback holds a reference to the same heap object, not a snapshot. This is a common cause of stale-data bugs in async code where a shared object is modified between scheduling and execution.

Examples

Primitives vs objects when passed to a function

javascript
function addTen(n) { n = n + 10; // modifies a local copy only } function addScore(obj) { obj.score = 100; // modifies the actual object } let num = 5; addTen(num); console.log(num); // 5, unchanged let user = { name: "Alice" }; addScore(user); console.log(user.score); // 100, changed

Pass a primitive and any mutation stays inside the function. Pass an object and the caller sees every change. I've watched this trip up junior developers who assumed JavaScript always makes a copy of what you pass in.

Extracting a primitive from a React prop

javascript
function UserProfile({ user }) { // Extracting a string primitive from the prop object const [name, setName] = useState(user.name); // Editing the input only updates local state // user.name stays untouched return <input value={name} onChange={e => setName(e.target.value)} />; }

useState(user.name) copies the string value, not a reference to user. Local edits never touch the original prop. If user itself were passed to useState, any mutation to that prop object would be shared with the parent component.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?