3

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?

Nathan Wall
  • 10,530
  • 4
  • 24
  • 47
  • 3
    As a precision : `[object type]` is the generic not overridden return of toString. MDN : https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toString – Denys Séguret Oct 31 '12 at 05:37
  • That doesn't help me with my question.. ? I already know what it does. – Nathan Wall Oct 31 '12 at 05:40
  • null... is an object I think. If you do typeof null, it should return "object". Even tho typeof undefined returns "undefined", I think undefined itself is an object in the javascript engine. – user1600124 Oct 31 '12 at 05:45
  • Neither `null` nor `undefined` are objects, despite what `typeof` says. They're primitives, and "non-coercible" primitives at that. (Brenadan Eich himself has referred to the `typeof null == 'object'` thing as a mistake.) – Nathan Wall Oct 31 '12 at 05:48
  • Neither `null` or `undefined` are objects in terms of JavaScript. It's important to note that JavaScript doesn't even have classes though. The way `null` and `undefined` are implemented however is such that they are singleton instances of the classes `Null` and `Undefined` in the language in which the JavaScript interpreter is written (usually C++). – Aadit M Shah Oct 31 '12 at 05:52
  • `[[Class]]` is definitely not a JavaScript value. So it's understandable that `object` also does not mean a JavaScript object. – Aadit M Shah Oct 31 '12 at 05:54
  • `[[Class]]` is a JavaScript value as defined in the ECMAScript spec. It makes no difference what language is used to implement the JavaScript engine. A JavaScript interpreter could be written in a classless, objectless language like QBASIC, and `Object.prototype.toString.call(null)` should still return `"[object Null]"` by the spec. – Nathan Wall Oct 31 '12 at 05:59
  • http://wiki.ecmascript.org/doku.php?id=harmony:typeof_null Found that... so they kept the behavior to avoid breaking down old code? – user1600124 Oct 31 '12 at 06:00
  • Maybe it's to preserver current code as well... Did some googling... It seems that lot of people use this method to check object types... If the format changes.. lots of code will get broken. Maybe that's why they kept the format till now. – user1600124 Oct 31 '12 at 06:08
  • @NathanWall - `[[Class]]` is not a JavaScript value, and it certainly is implementation specific. For example, `Object.prototype.toString.call(null)` returns `[object global]` in Rhino. – Aadit M Shah Oct 31 '12 at 06:12
  • Rhino is behind the times. It's an ES3 engine. [ES5.1](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) (section 15.2.4.2) specifically says `Object.prototype.toString` applied to `null` should return `[object Null]` and applied to `undefined` should return `[object Undefined]`. The reason Rhino says `global` is because it's impossible to `call` in the context of `null` in ES3, as it defaults to `global`. You're not really `call`ing on `null` in the first place. `[[Class]]` also is all over the ES 5.1 spec. – Nathan Wall Oct 31 '12 at 06:17

3 Answers3

3

Annex F of the ES5.1 specification says this about Object.prototype.toString:

15.2.4.2: Edition 5 handling of undefined and null as this value caused existing code to fail. Specification modified to maintain compatibility with such code. New steps 1 and 2 added to the algorithm.

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 expects Object.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.

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Allen Wirfs-Brock
  • 1,094
  • 1
  • 8
  • 10
  • Sweet, thanks for the answer! If this is really Allen Wirfs-Brock, I have a question for you. I'm working on some ES6 shims/etc and I tend to have questions from time-to-time understanding everything. I haven't wanted to bother the es-discuss group with my questions, but it's hard finding people here who can answer them (although benvie has been great help!). I wouldn't ask the kind of question you just answered on es-discuss, but I've wondered if I could ask a more serious question [like **this**](http://stackoverflow.com/questions/13151643/access-nativebrand-class-in-es6-ecmascript-6) there. – Nathan Wall Nov 01 '12 at 06:14
  • Less, this is really me...I think it would be fine to ask such questions on es-discuss. At the very least, such questions may point out areas where we need to add clarity to the ES6 spec. – Allen Wirfs-Brock Nov 01 '12 at 17:23
2

Okay, think of it like this:

  1. All values in JavaScript are actually instances of a class. Even undefined and null are implemented as singleton objects in the interpreter. For example undefined is actually Undefined.instance in Rhino (implemented in Java).
  2. The 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].
  3. Yes, the [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 class org.mozilla.javascript.BaseFunction. This class has a method called getClassName which returns Function (the [[Class]] of the instances of BaseFunction).

Similarly there's a class called org.mozilla.javascript.Undefined which has a static property called instance which is the singleton instance of Undefined.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • 1
    ECMA-262 does not provide any implementation details, only behaviour. `null` and `undefined` are [Types](http://ecma-international.org/ecma-262/5.1/#sec-8), they are not Objects (even though `typeof Null == 'object')`. There are no classes in ECMAScript. Implementations are free to extend objects, but don't expect all implementations to copy each other, or Mozilla's implementation (i.e. JavaScript). – RobG Oct 31 '12 at 07:16
1

Here is what ES5 says:

15.2.4.2 Object.prototype.toString ( )

When the toString method is called, the following steps are taken:

  1. If the this value is undefined, return "[object Undefined]".
  2. If the this value is null, return "[object Null]".
  3. Let O be the result of calling ToObject passing the this value as the argument.
  4. Let class be the value of the [[Class]] internal property of O.
  5. Return the String value that is the result of concatenating the three Strings "[object ", class, and "]".

So toString first deals with undefined and null, then converts the argument to an object, then returns the internal [[Class]] property of that object. Note that toString 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:

// Create number objects with the same value
var a = new Number(7);
var b = new Number(7);

a == b; // false

a.valueOf() == b.valueOf(); // true

A primary goal of JavaScript was to be simple. Loose typing makes life easier, but it also means there are some foibles to learn.

RobG
  • 142,382
  • 31
  • 172
  • 209