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.
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.
Thanks Jose this is a good insight – I will update. (glad you like the article!)
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.
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?
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
Yes, but O is used only to get the method. Then (at step 7):
And base is a primitive value.
Dmitry.
Very nice article, Angus! The returnMe and typeof interactions were all new to me, so I am very glad I read this!
Hasta,
Nick
@Nick – glad you liked it!
@Angus, @joseanpg:
Annex C: http://ecma262-5.com/ELS5_HTML.htm#Annex_C
Additionally: http://dmitrysoshnikov.com/ecmascript/es5-chapter-2-strict-mode/#this-value-restrictions
Dmitry.
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]
Yep Dmitry wins – good discussion guys! ( I added a note about strict mode to the article)
And another example, values vs. objects.
var s1 = new String(“ops”);
var s2 = new String(“ops”);
alert(s1 == s2);
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”
Whoops – thanks for the praise and for pointing out the typo
Great article. Thanks for sharing this post !
Very nice. Thank you.
I want an objectpotamus. Badly.
Reblogged this on dpeeva.