Search code examples
javascriptclassinheritanceprototype

Where does an object's toString method get its value from?


I'm writing some custom classes in JavaScript and I would like their toString method to return a value that reflects the class itself rather than the Object class. For example:

function MyClass(){}

var my_object=new MyClass();
alert(my_object);// Returns "[object Object]"

If I instantiate an HTMLImageElement the result is different:

var my_image=new Image();
alert(my_image);// Returns "[object HTMLImageElement]"

I can override the toString method for my custom class like this:

MyClass.prototype={
    toString:function(){
        return "[object MyClass]";
    }
};

Overriding the toString method allows me to get a result that reflects my class name, but I feel like this approach is not the same one used by the HTMLImageElement. Is it? Is there a way to change the result of toString without overriding it in the prototype? Are built in classes like HTMLImageElement actually JavaScript objects or are they something else?


Solution

  • When you use...

    function MyClass(){}
    var my_object = new MyClass();
    

    ... my_object.toString is inherited from Object.prototype:

    my_object.hasOwnProperty('toString');                      // false
    MyClass.prototype.hasOwnProperty('toString');              // false
    Object.prototype.hasOwnProperty('toString');               // true
    /* => */ my_object.toString === Object.prototype.toString; // true
    

    In the case of Image instances, they inherit toString from Object.prototype too:

    var my_image = new Image();
    my_image.hasOwnProperty('toString');                      // false
    HTMLImageElement.prototype.hasOwnProperty('toString');    // false
    HTMLElement.prototype.hasOwnProperty('toString');         // false
    Node.prototype.hasOwnProperty('toString');                // false
    EventTarget.prototype.hasOwnProperty('toString');         // false
    Object.prototype.hasOwnProperty('toString');              // true
    /* => */ my_image.toString === Object.prototype.toString; // true
    

    Object.prototype.toString is defined as

    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 "]".

    The difference is that the internal [[Class]] of my_object is "Object", but the internal [[Class]] of my_image is "HTMLImageElement".

    Therefore,

    • No, HTMLImageElement do not produce a custom string by overriding Object.prototype.toString, they use a custom [[Class]] instead.
    • But you can't use the same approach, at least in ECMAScript 5:

      8.6.2 Object Internal Properties and Methods

      This specification defines no ECMAScript language operators or built-in functions that permit a program to modify an object’s [[Class]] or [[Prototype]] internal properties