If you call Object.prototype.toString.call(anything)
the result is always [object Something]
, where Something
could be one of several things. My question is why is the "object" part there? It seems superfluous to always have it there. It doesn't tell you anything about the argument which was passed in.
Object.prototype.toString.call(null);
=> [object Null]
Object.prototype.toString.call(undefined);
=> [object Undefined]
Since null
and undefined
aren't objects (and fails CheckObjectCoercible
), the "object" part really, really seems meaningless.
I have to think there was some reason originally that the "object" part was put there, even if the reason has been lost in the time since, and it's now just preserved for historical reasons.
Can anyone help shed some light on this?
To be clear, I already know how Object.prototype.toString
can be used to get the [[Class]]
(type) of an object. My question concerns the reason for the format of the returned string -- specifically the "object" part at the beginning. I want to know if there was ever a reason for this part or if there is a possible future reason for this part. Maybe in the future it could return something other than "object"? Can I expect it to always return "object"? If so, why does it even return it? Who wants a function to always return the same thing no matter what the input is?
Okay, think of it like this:
undefined
andnull
are implemented as singleton objects in the interpreter. For exampleundefined
is actuallyUndefined.instance
in Rhino (implemented in Java).toString
method defined exposes the class name of the JavaScript value. However since the value is not the class itself but an instance of the class,toString
returns the a string of the form[object ClassName]
.[object *]
form is redundant. However someone wanted it to be like that and so we're stuck with it. Now that it's there it will cause a lot of problems if it's suddenly changed. Old code that depends upon it may break.Edit: It's important to note when JavaScript ends and the interpreter begins. For example when you call a function in JavaScript the interpreter creates a new execution context, which is an object in the language in which the JavaScript interpreter is written (not JavaScript itself).
Similarly, the
[[Class]]
of a JavaScript value is the class in the language in which the JavaScript interpreter is written, which is used to create instances of that class. For example, in Rhino functions are instances of the classorg.mozilla.javascript.BaseFunction
. This class has a method calledgetClassName
which returnsFunction
(the[[Class]]
of the instances ofBaseFunction
).Similarly there's a class called
org.mozilla.javascript.Undefined
which has a static property calledinstance
which is the singleton instance ofUndefined
.Here is what ES5 says:
So
toString
first deals withundefined
andnull
, then converts the argument to an object, then returns the internal[[Class]]
property of that object. Note thattoString
is intended to be inhertied by objects, so it's only fair that it behaves like it does.In ECMAScript, everything is an object. Primitives are just a convenience to avoid things like:
A primary goal of JavaScript was to be simple. Loose typing makes life easier, but it also means there are some foibles to learn.
Annex F of the ES5.1 specification says this about
Object.prototype.toString
:This is a correction that was made in the ES5.1 spec. Prior to ES5, passing null or undefined to toString always caused the global object to be passed instead. Strict mode related changes in ES5 cause null and undefined to be passed without modification. As specified in ES5, passing null or undefined to
Object.prototype.toString
caused a TypeError exception. This exception broke some existing code so we had to fix the spec to not throw in that case.So what should
toString
return for null and undefined? It turns out that a lot of existing code also expectsObject.prototype.toString
to always return a string of the form"[object *]"
. So, we decided to make null and undefined produce"[object Null]"
and"[object Undefined]"
.This seems to have worked and generally permitted existing ES3-based code to keep working in the presence of the strict mode related changes in ES5/5.1.