Search code examples
javascriptprototypejsprototypeprototype-programmingprototypal-inheritance

prototypal inheritance concept in javascript as a prototype based language


You know Javascript is a prototype-based programming language .
I have read some books about Javascript and its prototypal inheritance concept but:

"If you can't explain it to a six-year old, you really don't understand it yourself.”. Well, I tried to explain JavaScript prototype concept to a 22-year old friend and completely failed!

How would you explain it to a 6-year old person that is strangely interested in that subject?
I have seen some examples given in Stack Overflow, and it did not help.


Solution

  • Unlike most other object-oriented languages, JavaScript doesn’t actually have a concept of classes. In most other object-oriented languages you would instantiate an instance of a particular class, but that is not the case in JavaScript.
    In JavaScript, objects can create new objects, and objects can inherit from other objects.
    This whole concept is called prototypal inheritance.

    but how we can make an object?
    Simply you can create a generic object with {}.

    var a = {};
    a.prop = "myprop";
    console.log(a); //Object { prop="myprop" }
    

    you cannot create instance of a because it is not a function. in other words it has not special internal method [[Construct]].

    In JavaScript any function can also be instantiated as an object. The function below is a simple function which takes a name and saves it to the current context:

    function User( name ) {
       this.name = name;
    }
    

    We can see that User is instance of Function:

    alert(User instanceof Function); //true
    

    Create a new instance of that function, with the specified name:

    var me = new User( "My Name" ); 
    

    We can see that its name has been set as a property of itself:

    alert( me.name == "My Name" ); //true
    

    And that it is an instance of the User object:

    alert( me.constructor == User ); //true
    

    Now, since User() is just a function, what happens when we treat it as such?

    User( "Test" );
    

    Since its this context wasn't set, it defaults to the global window object, meaning that window.name is equal to the name provided:

    alert( window.name == "Test" ); //true
    

    The constructor property exists on every object and will always point back to the function that created it. This way, you should be able to effectively duplicate the object, creating a new one of the same base class but not with the same properties. An example of this can be seen below:

    var you = new me.constructor();
    

    We can see that the constructors are, in fact, the same:

    alert( me.constructor == you.constructor ); //true
    

    Prototype And Public Methods

    Prototype simply contains an object that will act as a base reference for all new copies of its parent object. Essentially, any property of the prototype will be available on every instance of that object. This creation/reference process gives us a cheap version of inheritance.
    Since an object prototype is just an object, you can attach new properties to them, just like any other object. Attaching new properties to a prototype will make them a part of every object instantiated from the original prototype, effectively making all the properties public. example:

    function User( name, age ){
       this.name = name;
       this.age = age;
    }
    

    Adding methods and properties to the prototype property of the constructor function is another way to add functionality to the objects this constructor produces. Let's add one more property, CardNo and a getName() method:

    User.prototype.CardNo='12345';
    User.prototype.getName = function(){
      return this.name;
    };
    

    And add another function to the prototype. Notice that the context is going to be within the instantiated object.

    User.prototype.getAge = function(){
       return this.age;
    };
    

    Instantiate a new User object:

    var user = new User( "Bob", 44 );
    

    We can see that the two methods we attached are with the object, with proper contexts:

    alert( user.getName() == "Bob" ); //true
    alert( user.getAge() == 44 ); //true
    

    So every function in javascript has a prototype property. Its initial value is an empty object ({}). Notice that generic objects (not functions) don't have the prototype property:

    alert( user.prototype ); //undefined (and it is not useful even you define it)
    

    Delegation

    When you try to access a property of user, say user.name the JavaScript engine will look through all of the properties of the object searching for one called name and, if it finds it, will return its value:

    alert( user.name );
    

    What if the javascript engine cannot find the property? It will identify the prototype of the constructor function used to create this object (same as if you do user.constructor.prototype). If the property is found in the prototype, this property is used:

    alert(user.CardNo); // "12345"
    

    and so...
    If you want to distinguish between the object's own properties versus the prototype's properties, use hasOwnProperty(). Try:

    alert( user.hasOwnProperty('name') ); //true
    alert( user.hasOwnProperty('CardNo') ); //false
    

    Private Methods

    When you directly set a property for a function, it will be private. example:

    function User()
    {
        var prop="myprop";
        function disp(){
           alert("this is a private function!");
        }
    }
    var we = new User();
    alert(we.prop); //undefined
    we.disp(); // Fails, as disp is not a public property of the object
    

    Privileged Methods

    Privileged methods is a term coined by Douglas Crockford to refer to methods that are able to view and manipulate private variables (within an object) while still being accessible to users as a public method. example:
    Create a new User object constructor:

    function User( name, age ) {
       //Attempt to figure out the year that the user was born:
       var year = (new Date()).getFullYear() – age;
    
       //Create a new Privileged method that has access to the year variable, but is still publically available:
       this.getYearBorn = function(){
          return year;
       };
    }
    

    Create a new instance of the user object:

    var user = new User( "Bob", 44 );
    

    Verify that the year returned is correct:

    alert( user.getYearBorn() == 1962 ); //true
    

    And notice that we're not able to access the private year property of the object:

    alert( user.year == null ); //true
    

    In essence, privileged methods are dynamically generated methods, because they’re added to the object at runtime, rather than when the code is first compiled. While this technique is computationally more expensive than binding a simple method to the object prototype, it is also much more powerful and flexible.

    Static Methods

    The premise behind static methods is virtually identical to that of any other normal function. The primary difference, however, is that the functions exist as static properties of an object. As a property, they are not accessible within the context of an instance of that object; they are only available in the same context as the main object itself. For those familiar with traditional classlike inheritance, this is sort of like a static class method.
    In reality, the only advantage to writing code this way is to keep object namespaces clean.
    A static method attached to the User object:

    function User(){}
    User.cloneUser = function( user ) {
       //Create, and return, a new user
       return new User( user.getName(), user.getAge() );
    };
    

    The cloneUser function is only accessible by User:

    var me = new User();
    me.cloneUser(me); //Uncaught TypeError: Object #<User> has no method 'cloneUser'