Search code examples
javascriptinheritanceprototype

Javascript prototype handling self and constructor parameters


Im trying to understand javascript prototyping principles and cant get some basic things to work. One thing that im trying to achieve is, to create a base object, that handles constructor input, and sets values based on that input, or default values if no constructor parameter is given.

Also, i cant quite figure out how to store this this in a variable so that it would point to correct instance of the object( parent object perhaps )

Below are 2 versions which i have tried to create basic inheritance. ( first one ive seen used before, but it doesnt allow me to handle constructor parameters passed to extended object using its base object's constructor. The second version...is something i came up with to enable it, but...i have never seen anyone using prototypes like this and im sure its the wrong way ( since the prototype property is a function instead of an object)

Whats the correct way of solving both of the problems.

var Person = function(conf) {
    if (!conf) {conf = {};}
    var _person = this;
    this.first_name = conf.first_name;
    this.last_name = conf.last_name;
    this.greet = function() {
        alert("Hi, im " + this.first_name + " " + this.last_name );
    }
    this.callback_greet = function() {
        alert("Hi, im " + _person.first_name + " " + _person.last_name );
        console.log("this:", this, " _person:", _person );
    }
}

var Student = function(conf) {
    if (!conf) {conf = {};}
    /* id like to pass this conf on to Person constructor */
    this.report = function() {
        alert(  this.first_name + " " + this.last_name + " is ready to study"  );
    }
}
Student.prototype = new Person();

var Teacher = function(conf) {
    if (!conf) {conf = {};}
    this.teach = function() {
        alert(  this.first_name + " " + this.last_name + " is ready to teach...maggots"  );
    }
}
Teacher.prototype = new Person();

student  = new Student({first_name: "Mike", last_name: "Stud"});
//student.first_name="Mike";
//student.last_name="Stud";
student.greet();
/* alerts Hi, im Mike Stud */


teacher  = new Teacher();
teacher.first_name="John";
teacher.last_name="Smith";
teacher.teach();
/* alerts John Smith is ready to teach...maggots */
teacher.callback_greet ();
/* both alerted values are undefined */

//_________________________
//_______Version 2  _______
//_________________________

var Person = function(conf) {
    if (!conf) {conf = {};}
    var _person = this;
    this.first_name = conf.first_name;
    this.last_name = conf.last_name;
    this.greet = function() {
        alert("Hi, im " + this.first_name + " " + this.last_name );
    }
    this.callback_greet = function() {
        alert("Hi, im " + _person.first_name + " " + _person.last_name );
        console.log("this:", this, " _person:", _person );
    }
}

var Student = function(conf) {
    if (!conf) {conf = {};}
    this.prototype = Person;
    this.prototype(conf);
    this.report = function() {
        alert(  this.first_name + " " + this.last_name + " is ready to study"  );
    }
}

var Teacher = function(conf) {
    if (!conf) {conf = {};}
    this.prototype = Person;
    this.prototype(conf);
    this.teach = function() {
        alert(  this.first_name + " " + this.last_name + " is ready to teach...maggots"  );
    }
}

var Principal = function(conf) {
    if (!conf) {conf = {};}
    this.prototype = Teacher;
    this.prototype(conf);
    this.dicipline_teachers = function() {
        alert(  this.first_name + " " + this.last_name + " thy all mighty principal is watching you"  );
    }
}

student  = new Student({first_name: "Mike", last_name: "Stud"});
student.greet();
/* alerts Hi, im Mike Stud */

teacher  = new Teacher({first_name: "John", last_name: "Smith"});
teacher.teach();
/* alerts John Smith is ready to teach...maggots */

principal  = new Principal({first_name: "David", last_name: "Faustino"});
principal.teach();/* alerts David Faustino is ready to teach...maggots */
principal.dicipline_teachers();/* David Faustino thy all mighty principal is watching you*/

Solution

  • Well, your second version is actually… somewhat… correct!

    What your snippet

    var Student = function(conf) {
        this.prototype = Person;
        this.prototype(conf);
    

    does here:

    1. Have a Student instance as this when being invoked with new Student()
    2. Create a property on the instance that contains a function (in this case, the parent constructor), which essentially creates a method on the instance
    3. Call that as a method. Which means, the Person function is invoked with its this pointing to the instance that we have here - and then Person does its setup on that instance.

    This is just what we want. Maybe except for creating the unnessary property. Notice that the name of this property being prototype is totally irrelevant for its function, you could have use myParentConstructor or so as well.

    In the standard JavaScript inheritance, we do a similar thing to that method call - we want to call the parent constructor on the current (child) instance so that it does get set up. However, we use the .call() method for that.


    Now we also want to use the prototype. In your code, all the methods greet, report, teach and dicipline_teachers could be shared amongst the instances, so they can - and should - go on the ConstructorFn.prototype. To let all teachers, students, and principals inherit these methods we need to set up a prototype chain (inheritance hierarchy). We don't want to use new Person as that would call the constructor and set up things like first_name on the prototype object where they would to be shared - but we don't want that. Instead, we use Object.create.

    Alltogether, your code would look like this:

    function Person(conf) {
        if (!conf) {conf = {};}
        var _person = this;
        this.first_name = conf.first_name;
        this.last_name = conf.last_name;
        this.callback_greet = function() {
            alert("Hi, im " + _person.first_name + " " + _person.last_name );
            console.log("this:", this, " _person:", _person );
        };
    }
    Person.prototype.greet = function() {
        alert("Hi, im " + this.first_name + " " + this.last_name );
    };
    
    function Student(conf) {
        Person.call(this, conf);
    }
    Student.prototype = Object.create(Person.prototype, {constructor:{value:Student}});
    Student.prototype.report = function() {
        alert(  this.first_name + " " + this.last_name + " is ready to study"  );
    };
    
    function Teacher(conf) {
        Person.call(this, conf);
    }
    Teacher.prototype = Object.create(Person.prototype, {constructor:{value:Teacher}});
    Teacher.prototype.teach = function() {
        alert(  this.first_name + " " + this.last_name + " is ready to teach...maggots"  );
    };
    
    function Principal(conf) {
        Teacher.call(this, conf);
    }
    Principal.prototype = Object.create(Teacher.prototype, {constructor:{value:Principal}});
    Principal.prototype.dicipline_teachers = function() {
        alert(  this.first_name + " " + this.last_name + " thy all mighty principal is watching you"  );
    };