This blog post is first in a series of two:
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.
This is an exhaustive list of JavaScript’s primitive values:
undefined
null
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.
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
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
);
The generic way of unwrapping a wrapper object is method .valueOf()
:
> new String('abc').valueOf()
'abc'
> new Number(123).valueOf()
123
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 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'
The following chapters in “JavaScript for impatient programmers” explain primitive values: