You'll remember that we mentioned that primitive values don't have methods as they are not objects. So, how exactly are we able to call toUpperCase on the preceding string? Is that not a method? Yes, it is. And to allow us to access this method, JavaScript wraps primitive values in their respective wrapper objects at the time of property access. This occurs for all primitive values, apart from null and undefined.
Primitive values, in these moments of being wrapped, remain immutable but, via their wrapped instance, provide access to properties and methods. A string value would be wrapped in a String instance, while a number value would be wrapped in a Number instance. The same would occur for all other non-null and non-undefined primitives. You are free to instantiate these wrapper objects yourself: you will observe that they no longer behave like primitives, though; they are objects, and, as such, you can add and mutate properties on them:
const name = new String('James');
// We can add arbitrary properties, since it is an object:
// (Warning: this is an anti-pattern)
name.surname = 'Padolsey';
name.surname; // => "Padolsey"
Invoking a wrapper constructor (for example, Number, String, and so on) as a regular function has a unique behavior. Instead of returning a new wrapper instance, it will cast the value to a particular type and return a regular primitive. This is quite useful when you're casting one type to another:
// Cast a number to a string:
String(123); // => "123"
// Cast a string to a number
Number("2"); // => 2
// Cast a number to a boolean
Boolean(0); // => false
Boolean(1); // => true
Invoking wrapper constructors as functions, as we have done here, is a useful casting technique, though it's not the only one. We'll cover typecasting and coercion in a lot more detail in Chapter 7, Dynamic Typing.