In JavaScript, every value is either a primitive or an object.
- Primitives are immutable, passed by value, and cannot have properties.
- Objects are everything else, including arrays, functions, dates, and regular objects. These are mutable and passed by reference. Objects can have properties and methods.
Meet the primitives
The latest ECMAScript standard (ECMAScript 2024 / ES15) defines seven primitive types.
Undefined
A variable that has been declared but not assigned a value has the undefined
value:
let x;
console.log(x); // undefined
Null
The null
literal signifies the intentional absence of any value:
let reason = null; // there is no reason
console.log(reason); // null
Boolean
Two Boolean literals represent logical truth values:
true
false
Number
All numeric values (including integers, floats, NaN
, and Infinity
) are of the Number type:
let a = 42; // integer
let b = 3.14; // float
let c = NaN; // yes, the "not a number" value is a number
let d = Infinity; // and beyond!
String
A sequence of characters, such as some words or text:
let greeting = "Hello";
let name = 'World';
let message = `Hi, ${name}!`;
BigInt
Represents integers of arbitrary length, created by appending n
to the end of an integer literal:
let big = 1234567890123456789012345678901234567890n;
Symbol
A unique (within the runtime environment) value that can be used as object property keys:
let sym = Symbol("id");
What's your type?
The typeof
operator returns a string indicating the operand type. There are seven possible return values for primitive types.
console.log(typeof undefined); // undefined
console.log(typeof true); // boolean
console.log(typeof 42); // number
console.log(typeof 9007199254740991n); // bigint
console.log(typeof "hello"); // string
console.log(typeof Symbol()); // symbol
// and then there's null... read more below
console.log(typeof null); // object?
null
is a special case
I want to call attention to a common point of confusion in JavaScript: the null
value. However, it is not an object and has no object wrapper. However, it blatantly lies and claims to be an object when you use the typeof
operator on it.
let nope = null;
console.log(typeof nope); // object (no, it's not)
console.log(nope.toString()); // TypeError: Cannot read properties of null (reading 'toString')
nope.x = 1; // TypeError: Cannot set properties of null (setting 'x')
Brendan Eich, the Creator of JavaScript, admitted that typeof null === "object"
is a bug, but fixing it would break a lot of code on the web. So, we must always work around this bug.
// randomly assign either an object or null
const value = Math.random() < 0.5 ? {} : null;
// don't forget to check for null
if (typeof value === "object" && value !== null) {
console.log("The value is a (real) object.");
}
Dressing primitives up like objects
Primitive values are not objects. They are immutable and cannot have methods or properties. You might be surprised by this statement, after all, it's pretty standard to see code that appears to call methods on and get/set properties on primitive values.
console.log("hello".length); // 5
console.log("hello".toUpperCase()); // HELLO
console.log(10.0.toFixed(2)); // 10.00
console.log(true.toString()); // true
What's happening here? The primitive types in the preceding example each have a corresponding object wrapper they can climb into whenever they need to do object-like things. For example, you can wrap a string primitive in a String
object by calling new String("hello")
. Think of the resulting object as a box with the primitive inside. You can always get the primitive value back by calling valueOf
on the object.
JavaScript performs automatic "boxing" for you by temporarily wrapping any String, Number, or Boolean values in an object whenever you try to access properties or methods. For example, "hello".toUpperCase()
works because JavaScript creates a String
object behind the scenes before calling its toUpperCase
method.
Wrapping primitives
JavaScript doesn't always wrap primitives in Objects for you. For example, undefined
and null
can't be wrapped.
undefined.toString(); // TypeError: Cannot read properties of undefined (reading 'toString')
null.toString(); // TypeError: Cannot read properties of null (reading 'toString')
If you try to force them into object boxes explicitly, their values disappear.
Object(null); // returns an empty object {}
Object(undefined); // returns an empty object {}
Strings, Numbers, and Booleans have dedicated object wrappers you can call yourself if you wish. In practice, there are very few reasons for doing this, but you might find some APIs that require objects when all you have are primitives, for example, when creating an entry in a WeakMap that requires an object for the key.
const s = new String("hello");
console.log(typeof s); // "object"
console.log(s instanceof String); // true
console.log(s.valueOf()); // "hello" (gets the primitive value)
console.log(s); // calls valueOf for you
Similarly, you can explicitly wrap numbers and booleans.
const n = new Number(42);
const b = new Boolean(true);
The last two primitives don't have their own dedicated object wrapper but can be objectified using the Object()
function if you need to.
const big = Object(123n);
console.log(typeof big); // "object"
console.log(big instanceof BigInt); // true
console.log(big.valueOf()); // "123n" (gets the primitive value)
console.log(big); // [BigInt: 123n] (calls toString)
const sym = Object(Symbol("key"));
console.log(sym); // [Symbol: Symbol(key)]
Top comments (0)