The Secret Life of JavaScript Primitives

You may not know it but, in JavaScript, whenever you interact with string, number or boolean primitives you enter a hidden world of object shadows and coercion. So dust off your Sherlock Holmes outfit and read on…

The basics

Objects are aggregations of properties. A property can reference an object or a primitive. Primitives are values, they have no properties.

In JavaScript there are 5 primitive types: undefined, null, boolean, string and number. Everything else is an object. The primitive types boolean, string and number can be wrapped by their object counterparts. These objects are instances of the Boolean, String and Number constructors respectively.

typeof true; //"boolean"
typeof Boolean(true); //"boolean"
typeof new Boolean(true); //"object"
typeof (new Boolean(true)).valueOf(); //"boolean"

typeof "abc"; //"string"
typeof String("abc"); //"string"
typeof new String("abc"); //"object"
typeof (new String("abc")).valueOf(); //"string"

typeof 123; //"number"
typeof Number(123); //"number"
typeof new Number(123); //"object"
typeof (new Number(123)).valueOf(); //"number"

.

If primitives have no properties, why does "abc".length return a value?

Because JavaScript will readily coerce between primitives and objects. In this case the string value is coerced to a string object in order to access the property length. The string object is only used for a fraction of second after which it is sacrificed to the Gods of garbage collection – but in the spirit of the TV discovery shows, we will trap the elusive creature and preserve it for further analysis…

String.prototype.returnMe= function() {
    return this;
}

var a = "abc";
var b = a.returnMe();  

a; //"abc" 
typeof a; //"string" (still a primitive)
b; //"abc"
typeof b; //"object"

…and as with many well meaning scientific investigations we have now interfered with the natural progression of things and prevented the object from being garbage collected so long as b is around. Heisenberg is alive and well 😉

(Note that in strict mode, the elusive creature gets away – thanks @DmitrySoshnikov)

Here’s a more environmentally responsible example that verifies the object type without interfering with garbage collection:

Number.prototype.toString = function() {
    return typeof this;
}

(123).toString(); //"object"

.
By this means primitives have access to all the properties (including methods) defined by their respective object constructors.

And these objects can also be coerced to values?

Yes. Mostly. Objects of this type are merely wrappers, their value is the primitive they wrap and they will generally coerce down to this value as required. For full details see this article.

//object coerced to primitive 
var Twelve = new Number(12); 
var fifteen = Twelve + 3; 
fifteen; //15
typeof fifteen; //"number" (primitive)
typeof Twelve; //"object"; (still object)

//another object coerced to primitive
new String("hippo") + "potamus"; //"hippopotamus" 

//object not coerced (because 'typeof' operator can work with objects)
typeof new String("hippo") + "potamus"; //"objectpotamus"

Sadly boolean objects do not coerce so easily. And, to add insult to injury, a boolean object evaluates to true unless its value is null or undefined. Try this:

if (new Boolean(false)) {
    alert("true???"); 
}

Usually you have to explicitly ask boolean objects for their value. The following might be useful for determining whether the value is “truthy” of “falsey”….

var a = "";
new Boolean(a).valueOf(); //false

…but in practice its easier to do this…

var a = Boolean("");
a; //false

..or even this…

var a = "";
!!a; //false

.

Does coercion allow me to assign values to primitives?

No.

var primitive = "september";
primitive.vowels = 3;

primitive.vowels; //undefined; 

If JavaScript detects an attempt to assign a property to a primitive it will indeed coerce the primitive to an object. But, as with the previous examples, this new object has no references and will immediately become fodder for garbage collection.

Here’s a pseudo-code representation of the same example to illustrate what really happens

var primitive = "september";
primitive.vowels = 3;
//new object created to set property 
(new String("september")).vowels = 3;

primitive.vowels;
//another new object created to retrieve property 
(new String("september")).vowels; //undefined

So as you can see, its not only useless but pretty wasteful.

Wrap up

It turns out that the ability to assign properties is just about the only advantage of objects over their primitive counterparts, but in practice even this is a dubious quality. Strings, booleans and numbers have specific and well defined purposes and redefining them as state holders is likely just going to confuse people. Moreover, since primitives are immutable you cannot modify them by tweaking the properties of the object wrapper:

var me = new String("Angus");
me.length = 2; //(error in strict mode)
me.length; //5 (not 2 - thanks @joseanpg)
me.valueOf(); "Angus"

 
Nevertheless, I do think that a good understanding of primitives and what happens under the covers when you interact with them is an important step towards a deeper knowledge of the language. I hope this helped.

24 thoughts on “The Secret Life of JavaScript Primitives

  1. Very good article! Your blog is a nice source of JavaScript wisdom and funny style (“… the Gods of garbage collection – but in the spirit of the TV discovery shows, we will trap the elusive creature and preserve it for further analysis” :^D)

    I would like to make a small contribution: stringObjectInstance.length have DontEnum,
    DontDelete, ReadOnly attributes in ES3 ( [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false in ES5), therefore in the snippet

    var me = new String("Angus");
    me.length = 2;
    me.length; //2
    me.valueOf(); "Angus"

    the second statement will do nothing in ES3 or non-strict ES5, and throws a TypeError in strict ES5. In the third stament that 2 must be a 5. The source of the confusion may lie in the fact that an assignment returns the rvalue, and this value is usually displayed by the console.

  2. Notice, that in strict mode of ES5, a this value isn’t coerced to an object. I.e. in your var b = a.returnMe(); , “b” is still a string in this case, but not the object.

    Good write up.

    Dmitry.

    1. Thanks Dmitry – here is why I thought even in strict mode this will be an object

      See ECMA 8.7.1
      The following [[Get]] internal method is used by GetValue when V is a property reference with a primitive base value
      1. Let O be ToObject(base).
      2 Let desc be the result of calling the [[GetProperty]] internal method of O with property name P
      etc.

      There is no mention of strict mode exception

      All this happens before 10.4.3 (where this is only converted to object in non strict mode). So I thought strict mode does not matter in this case since this is already an object

      Is this right?

  3. Dmitry, Angus is right. In 11.2.3 you can see:
    The production CallExpression : MemberExpression Arguments is evaluated as follows:
    -Let ref be the result of evaluating MemberExpression.
    -Let func be GetValue(ref).

    And in 8.7.1:

    4. If IsPropertyReference(V), then
    a. If HasPrimitiveBase(V) is false, then let get be the [[Get]] internal
    method of base, otherwise let get be the special [[Get]] internal
    method defined below
    .

    Below:
    1. Let O be ToObject(base).

    Therefore, the this value received by [[Call]] is an String Object

  4. Dmitry is right:

    11.2.3 Function Calls
    1. Let ref be the result of evaluating MemberExpression.
    2. Let func be GetValue(ref). [ToObject]
    6. If Type(ref) is Reference, if IsPropertyReference(ref) is true, then
    let thisValue be GetBase(ref). [primitive]

  5. Good job. Very readable.

    You have a typo in the first listing though, you have:

    typeof Number(“abc”); //”number”

    and you meant to have:

    typeof Number(123); //”number”

Leave a comment