What are wrapper objects for primitive values?

[2022-02-20] dev, javascript, jslang
(Ad, please don’t block)

This blog post is first in a series of two:

  1. What are wrapper objects for primitive values?
  2. How do primitive values get their properties?

Each of the primitive types boolean, number, bigint, string and symbol has an associated wrapper class (Boolean, Number, BigInt, String, Symbol). In this blog post, we examine what these classes are good for.

Wrapper classes for primitive types  

This is an exhaustive list of JavaScript’s primitive values:

  • undefined
  • null
  • booleans
  • numbers
  • bigints
  • strings
  • symbols

Each primitive type (except for the types of undefined and null) has a corresponding wrapper class:

  • Boolean
  • Number
  • BigInt
  • String
  • Symbol

The key purpose of these classes is to provide properties (mostly methods) for primitive values. We’ll see how exactly that works in the second of this series of blog posts. This is an example:

> 'abc'.toUpperCase === String.prototype.toUpperCase
true

There are two ways of invoking wrapper classes:

  • All wrapper classes can be function-called, which converts an arbitrary value to the primitive type that the class represents. This is a descriptive way of converting to primitive types and I recommend it.

  • Only the wrapper classes Boolean, Number, and String can be instantiated via new. Doing this (explicitly) is almost never useful for programmers.

Instantiating wrapper classes  

The wrapper classes Boolean, Number, and String can be instantiated via new:

> new String('abc') instanceof String
true

That is, new String() wraps a primitive string and produces a wrapper object.

The wrapper classes BigInt (ES2020) and Symbol (ES6) are relatively new and can’t be instantiated:

> new BigInt(123)
TypeError: BigInt is not a constructor
> new Symbol('MY_SYMBOL')
TypeError: Symbol is not a constructor

Generically wrapping primitive values  

In addition to wrapping a primitive value by new-invoking a wrapper class, we can also do so generically by function-calling Object (the class of most objects):

assert.equal(
  Object('abc') instanceof String, true
);
assert.equal(
  Object(123) instanceof Number, true
);

With Object(), we can even create instances of BigInt and Symbol (even though those classes can’t be new-invoked):

> Object(123n) instanceof BigInt
true
> Object(Symbol('MY_SYMBOL')) instanceof Symbol
true

As an aside, if the argument of Object() is an object, it is simply returned without any changes:

const obj = {};
assert.equal(
  Object(obj), obj
);

Unwrapping primitive values  

The generic way of unwrapping a wrapper object is method .valueOf():

> new String('abc').valueOf()
'abc'
> new Number(123).valueOf()
123

Primitive values vs. wrapper objects  

Interestingly, the primitive value 'abc' is not an instance of the wrapper class String:

> 'abc' instanceof String
false
> new String('abc') instanceof String
true

typeof also shows us that 'abc' is primitive, but new String('abc') is an object:

> typeof 'abc'
'string'
> typeof new String('abc')
'object'

That is, the values of a primitive type are different from the instances of the associated wrapper class. In day-to-day programming, I pretend that wrapper objects don’t exist. They are used under the hood but rarely useful elsewhere.

For more information on the differences between primitive values and objects, see “JavaScript for impatient programmers”.

Function-calling wrapper classes  

Function-calling wrapper classes provides us with a descriptive way of converting arbitrary values to primitives:

> Boolean(0)
false
> Number('123')
123
> BigInt(123)
123n
> String(123)
'123'

Symbol() has a special status in that it is more of a factory function for symbols (whose parameter is a string that describes the created symbol) than a conversion function:

> typeof Symbol('MY_SYMBOL')
'symbol'

Function-applying a wrapper class to one of its instances, unwraps that instance:

> Number(new Number(123))
123
> String(new String('abc'))
'abc'

Further reading  

The following chapters in “JavaScript for impatient programmers” explain primitive values: