Search code examples
javascriptprototypejs

JavaScript prototype and call()


I am learning the js prototype, and I am wondering if there are any differences between the following two segment?

Segment1:

function SuperType(){
    this.color=["blue","yellow"];
}


function SubType(){

}      
Subtype.prototype = new SuperType();

Segment2:

function SuperType(){
    this.color=["blue","yellow"];
}

function SubType(){
    SuperType.call(this);
}   

and if the above two does the same thing, then why some codes bother to do this:

function SubType(){

        SuperType.call(this); 
        }

SubType.prototype=new SuperType(); 

Solution

  • Yes, there are differences.

    In Segment 1, the subclass SubType does not call the constructor of SuperType so it never gets executed. Segment 1 is not a correct general purpose way to inherit from another object.

    In Segment 2, the subclass SubType does call the constructor of SuperType so the statement this.color=["blue","yellow"]; gets executed, but the prototype is not set appropriately so any prototype items that SuperType might have had are not inherited. Segment 2 is not a correct general purpose way to inherit from another object.

    Segment 1 would work properly if there was no code in the SuperType constructor (not the case in your example). As you show it, because the SuperType() constructor is not called, this.color would only be in the prototype and thus the same array would be shared by all instances which is generally NOT what you want. Calling the constructor properly would give every instance it's own copy of this.color, not a shared copy.

    Segment 2 would work properly if nothing was added to the SuperType prototype and there are no constructor arguments for SuperType (happens to be the case in your example, but not a good general practice).

    Neither is a good general purpose way of doing things.


    Your last option is the proper general purpose way to do things because it both inherits from the prototype AND it executes the constructor of the inherited object.


    The most general purpose way also passes any constructor arguments to the inherited object using .apply(this, arguments) like this and initializes its own prototype and sets it's constructor property.

    // define base object constructor
    function SuperType(){
        this.color=["blue","yellow"];
    }
    
    // define base object methods on the prototype
    SuperType.prototype.foo = function() {};
    
    // ---------------------------------------------------------------------------
    
    // define object that wants to inherit from the SuperType object
    function SubType() {
        // call base object constructor with all arguments that might have been passed
        SuperType.apply(this, arguments); 
    }
    
    // set prototype for this object to point to base object
    // so we inherit any items set on the base object's prototype
    SubType.prototype = new SuperType();
    // reset constructor to point to this object not to SuperType
    SubType.prototype.constructor = SubType;
    
    // define any methods of this object by adding them to the prototype
    SubType.prototype.myMethod = function() {};
    

    If you are willing to only support IE9 and above, then you should change this line:

    SubType.prototype = new SuperType();
    

    to this:

    SubType.prototype = Object.create(SuperType.prototype);
    

    This avoids calling the SuperType() constructor in order to just get an object to use for the prototype. In most cases this really doesn't matter, but in cases where the constructor has side effects outside of just initializing it's own properties, it can matter.