skip to content

Name the seven primitive types in JavaScript and explain what makes a value a primitive rather than an object.

level: juniorimportance: must knowfreq 80%

answer

  1. seven, plus one more language type
  2. two of them mean "no value"
  3. the newest two arrived in ES2015 and ES2020
  4. no own slots, nothing to mutate
  5. arrays and functions are not on the list

basics

~10 s

JavaScript has seven primitives: string, number, boolean, null, undefined, symbol and bigint. Everything else is an object. Primitives are immutable, hold no properties of their own, and are copied and compared by their value.

solid answer

~40 s

The seven primitive types are `string`, `number`, `boolean`, `null`, `undefined`, `symbol` and `bigint`. `Object` is the eighth language type, and every value that is not one of the seven is an object — arrays, functions, dates, regular expressions, `Map`, `Set` and promises included. What distinguishes a primitive is that it is immutable and has no own state you can write to: `'abc'.toUpperCase()` hands back a new string and leaves the original alone, and there is no way to change the value 1 into something else. Primitives are also copied by value, so two variables holding `1` or `'abc'` are completely independent. `symbol` arrived in ES2015 and `bigint` in ES2020, which is why older material still says JavaScript has five or six primitives.

go deeper

for a junior

Be able to list all seven by name without hesitating, and say plainly that everything else — arrays, functions, dates — is an object. Add that primitives cannot be changed in place.

for a middle

Explain the three behaviours behind the label: no own properties, immutability, and copy-by-value. Be ready to show with two lines of code why two variables holding the same object are not independent while two holding the same number are.

for a senior

Connect the split to the bugs it causes in running systems: shared references leaking state between callers, and code that calls a string method without reassigning the result. Explain which absence value you standardise on in an API and why.

for a principal

Own the convention. Decide whether your codebase uses null or undefined for absence, whether functions may mutate objects they receive, and how those rules are enforced in review and lint config — inconsistency here produces defects long after the original author has moved on.

## The two-bucket model At runtime every JavaScript value falls into exactly one of two buckets: it is a **primitive**, or it is an **object**. The specification calls these the language types, and there are eight of them — the seven primitive types plus `Object`. There is no third category. Arrays, functions, `Date` instances, regular expressions, `Map`, `Set`, promises and errors are all objects; they differ only in which prototype they delegate to and which internal machinery they carry. ## The roster - **string** — an immutable sequence of UTF-16 code units: `'abc'`, `"abc"`, `` `abc` ``. - **number** — one type for every ordinary numeric value, a 64-bit IEEE-754 double: `42`, `3.14`, `-0`, `Infinity`, `NaN`. - **boolean** — exactly two values, `true` and `false`. - **undefined** — the absence the *language itself* produces. - **null** — the absence a *programmer* writes deliberately. - **symbol** (ES2015) — a value guaranteed unique, created with `Symbol('label')`, usable as a property key that cannot collide with any other. - **bigint** (ES2020) — an arbitrary-precision integer written with an `n` suffix: `9007199254740993n`. That is the complete list. If a candidate offers `array`, `function`, `object` or `date` as an eighth primitive, they have merged the two buckets. ## What actually makes a value primitive Three observable properties, all of which an interviewer can probe with two lines of code. **No own state.** A primitive is just the value; it has no slots to write into. ```js let s = 'abc'; s[0] = 'X'; console.log(s); // 'abc' — the write went nowhere ``` **Immutability.** Nothing in the language mutates a primitive in place. Every string, number and bigint operation *produces a new value*: ```js let t = 'abc'; t.toUpperCase(); // returns 'ABC' console.log(t); // still 'abc' t = t.toUpperCase(); // you must rebind to keep the result ``` Be precise about the wording here: the *value* is immutable, not the *variable*. `let t` can be rebound freely; what cannot happen is the string `'abc'` itself changing. **Copy by value.** Assignment hands over the value itself, so the two names have no ongoing relationship: ```js let a = 1; let b = a; b = 2; console.log(a); // 1 ``` Objects behave differently precisely because the value being copied is a reference to a single shared thing: ```js const o = { n: 1 }; const p = o; p.n = 2; console.log(o.n); // 2 — one object, two names ``` ## Two different kinds of nothing Both `undefined` and `null` are primitives, and both mean "no value", but they come from different places. `undefined` is what the runtime supplies when it has nothing to give: a declared-but-unassigned variable, a parameter the caller omitted, a property that was never set, a function that falls off the end without `return`. `null` is never handed to you by ordinary evaluation — a programmer (or a library author) chose it to say "intentionally empty". A handful of standard-library calls do return it as a deliberate signal, for example `String.prototype.match` when the pattern does not match, and `Object.getPrototypeOf` at the end of a prototype chain. ## Why the roster is worth memorising Almost every later JavaScript question builds on it. Coercion is defined as conversions *between primitives*, and objects are converted to a primitive first before arithmetic or comparison. The mutation bugs that dominate real code all come from the object side of the split — one object, several names. And the two newest members exist because the primitive set had gaps: `symbol` gives you a property key that cannot be guessed or collided with, and `bigint` gives you integers past the range a double can represent exactly. Both are primitives in the full sense: immutable, stateless, copied by value. ## A note on how the roster grew ES5 had five primitives; ES2015 added `symbol`; ES2020 added `bigint`. If an older tutorial says "six types", it is not wrong for its edition — it is out of date. Saying "seven, and `Object` is the eighth language type" is the answer that lands today.

  • Is a function a primitive value in JavaScript?
    No. A function is an object that happens to be callable. It delegates to `Function.prototype`, carries properties such as `name` and `length`, and you can attach your own (`fn.cached = true`). Because it is an object, it is copied by reference, so two variables holding the same function point at one object.
  • If primitives are immutable, what does `'abc'.trim()` do to the original string?
    Nothing. `trim`, `toUpperCase`, `slice`, `replace` and the rest all return a brand-new string and leave the receiver untouched. If you do not capture or reassign the result, the work is discarded — `s.trim();` on its own line is a no-op, whereas `s = s.trim();` is what people usually meant to write.
  • Why does JavaScript list Object as a language type alongside the seven primitives rather than as one of them?
    Because objects have the properties primitives lack: mutable own state, identity distinct from content, and reference copy semantics. Two separately created objects with identical contents are still two different values, while two separately produced strings with the same characters are the same value. That behavioural split is exactly the boundary the type list draws.

saying these in an interview costs you the question

  • Counts Array or Function among the seven primitives
  • Forgets that symbol and bigint exist
  • Says every value in JavaScript is an object
  • Claims a string can be changed in place
  • Treats null and undefined as one and the same value

context