Methods of primitives
You can write "hello".toUpperCase() and get back "HELLO". That looks like you called a method on an object. But a string is a primitive, not an object. So how does a plain value suddenly grow methods? That trick is what this chapter is about, and understanding it clears up a whole class of confusing behavior later on.
Start with the two kinds of thing you’re dealing with.
A primitive:
- Is a single value of a primitive type.
- Comes in 7 types:
string,number,bigint,boolean,symbol,null, andundefined.
An object:
- Can hold many values, stored as named properties.
- Is created with
{}, for example{name: "Maya", age: 30}. Objects come in other shapes too — functions, for instance, are objects.
The headline feature of objects is that a property can hold a function. When it does, we call that property a method:
let maya = {
name: "Maya",
sayHi: function() {
alert("Hi buddy!");
}
};
maya.sayHi(); // Hi buddy!
Here maya is an object with a method sayHi. Call it with maya.sayHi() and it runs.
mayastrJavaScript ships with many built-in objects already — ones for dates, errors, HTML elements, and more. Each carries its own set of properties and methods.
That power isn’t free. Objects are heavier than primitives. Each one needs extra machinery behind the scenes to track its properties, which costs memory and time.
A primitive that acts like an object
Whoever designed JavaScript ran into a genuine conflict:
- People want to do a lot with strings and numbers — uppercase a string, round a number, and so on. Reaching for those operations as methods (
str.toUpperCase()) feels natural. - Primitives need to stay fast and small. Making every string a full object would defeat the point.
The resolution is a little odd, but it works cleanly:
- Primitives stay primitive. One value, nothing bolted on.
- The language still lets you reach for methods and properties on strings, numbers, booleans, symbols, and bigints.
- To make step 2 possible, when you access a property, JavaScript builds a temporary object wrapper that carries the value and the relevant methods, runs what you asked, then throws the wrapper away.
Each primitive type gets its own wrapper, and each provides a different set of methods: String, Number, Boolean, Symbol, and BigInt.
Take the string method str.toUpperCase(), which returns an uppercased copy of the string:
let str = "Hello";
alert( str.toUpperCase() ); // HELLO
Nothing surprising in the output. The interesting part is what happens between the dot and the call:
stris a primitive. The moment you touch a property on it, a temporaryStringwrapper object is created that knows the value"Hello"and carries methods liketoUpperCase().- That method runs on the wrapper and returns a brand-new string, which
alertshows. - The wrapper is destroyed.
stris still the same lightweight primitive it always was.
So you get object-style methods while the value stays a lean primitive.
Engines don’t take this literally. The specification describes a wrapper being created and destroyed, but a real engine will often skip building the object entirely and just do the work directly — as long as the observable result matches what the spec says. You get the convenience with none of the object overhead in practice.
Numbers have their own methods. For example, toFixed(n) rounds a number to n digits after the decimal point and returns the result as a string:
let n = 1.23456;
alert( n.toFixed(2) ); // 1.23
There are many more of these. The Numbers and Strings chapters go through the useful ones in detail.
number → Number
bigint → BigInt
boolean → Boolean
symbol → Symbol
undefined
Summary
- Every primitive except
nullandundefinedcomes with a set of handy methods. The Numbers and Strings chapters cover the ones you’ll use. - Under the hood these methods run through a temporary wrapper object, but engines optimize the pattern so aggressively that calling them costs you effectively nothing.
nullandundefinedhave no methods at all — reaching for a property on either one throws aTypeError.- You can build wrapper objects yourself with
new Number(...)and friends, but don’t: the result is a truthy object, not a primitive, and it will surprise you.