Kunal Verma
Kunal Verma

Reputation: 51

Inheritance in Javascript: hasOwnProperty()

I was looking into the concepts of inheritance in javascript. Consider the following snippet:

var Person=function(firstname,lastname,gender){
  this.firstname=firstname;
  this.lastname=lastname;
  this.gender=gender;
  this.name=function(){
    return this.firstname +" "+this.lastname;
  }
}

var Employee = function(firstName, lastName, gender, title) {
  Person.call(this, firstName, lastName, gender);
  this.title = title;
};

var e = new Employee('Abc', 'efg', 'Tree', 'CEO');

Now, if we check the following:

console.log(e.hasOwnProperty('firstName') ) ==> TRUE

I want to know is there any way so that we can inherit the values from parents completely? Such that

console.log(e.hasOwnProperty('firstName') ) ==> False

Upvotes: 0

Views: 424

Answers (2)

Amardeep Bhowmick
Amardeep Bhowmick

Reputation: 16908

This is completely fine, you are inheriting the properties of the Person through the call to the parent constructor Person.call(this, ...) and you are passing the this of the Employee instance to the Person constructor function.

When you invoke call and pass the this context of the Employee instance, it will actually use the this of the Employee to do these assignment operations:

this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
this.name = function(){
   return this.firstname +" "+this.lastname;
}

Here this refers to the new Employee instance. Since the above assignment makes these properties appear on the new Employee instance as own properties the hasOwnProperty returns true when you use any of these properties with the hasOwnProperty method.

This is the correct way to do inheritance for data properties in JavaScript. In case of methods we should put them in the prototype.

This is because the data properties should not be shared between the different instances of the Employee but the methods can, as they imply behaviour which can be common.

If you do not put the methods in the prototype and put them as own properties, it would become an overhead as the same logic would be present but the method would be a different instance for every instance of your constructor.

To complete your example, the hasOwnProperty is returning false for the name method as it is on the prototype, while other data properties will return true:

var Person = function(firstname, lastname, gender) {
  this.firstname = firstname;
  this.lastname = lastname;
  this.gender = gender;
}

Person.prototype.name = function() {
  return this.firstname + " " + this.lastname;
}

var Employee = function(firstName, lastName, gender, title) {

  Person.call(this, firstName, lastName, gender);

  this.title = title;

};

Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;

var e = new Employee('Abc', 'efg', 'Tree', 'CEO');

//Prints true
console.log(e.hasOwnProperty('firstname'));
//Prints false
console.log(e.hasOwnProperty('name'));

The Function.prototype.call basically uses the supplied this context, it has nothing to do with inheritance in general. Basically you are re-using the Person constructor function logic and populating the data property values of the Employee instance when you do this.

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074515

It's important to understand here that there's just one object being created by new Employee. That object has a combination of features defined for it by Person and by Employee, but it's one object. All of the this.x = y; statements in the constructors create own properties of that object. In fact, that particular example doesn't define anything useful for it to inherit, since nothing is put on the objects that will be in its prototype chain. (The prototype chain is also a bit malformed, see my answer here for the correct way to hook up Employee and Person in ES5 and also in ES2015+.)

You can do it differently such that there's an object for the Person parts and another object inheriting from it for the Employee parts, but if you want to go down that route, you probably want to stop using constructor functions (ones you use new with) and use builder functions instead, because the assumptions and standard practices around constructor functions expect a single combined object.

Here's a minimal example of doing it that way:

function createPerson(firstname, lastname, gender) {
    const person = {};
    person.firstname = firstname;
    person.lastname = lastname;
    person.gender = gender;
    person.name = function() {
      return person.firstname + " " + person.lastname;
    };
    return person;
}

function createEmployee(firstName, lastName, gender, title) {
    const person = createPerson(firstName, lastName, gender);
    const employee = Object.create(person); // <=== The key bit
    employee.title = title;
    return employee;
}

const e = createEmployee("Abc", "efg", "Tree", "CEO");

console.log(e.hasOwnProperty("firstname")); // false
console.log(e.hasOwnProperty("title"));     // true

e.firstname = "Joe";
console.log(e.hasOwnProperty("firstname")); // true

This key bit:

const employee = Object.create(person); // <=== The key bit

...creates an object that uses person as its prototype. So now the Person stuff is on one object and the Employee stuff is on another object that inherits from it.

But note that bit at the end&nbsb;— if you do:

e.firstname = "Joe";

...it will set the property on e, not its prototype, so at that point e has its own property called firstname. You can fix that using accessor properties:

function createPerson(firstname, lastname, gender) {
    const person = {
        get firstname() {
            return firstname;
        },
        set firstname(value) {
            firstname = value;
        },
        get lastname() {
            return lastname;
        },
        set lastname(value) {
            lastname = value;
        },
        get gender() {
            return gender;
        },
        set gender(value) {
            gender = value;
        },
        name() {
            return person.firstname + " " + person.lastname;
        },
    };
    return person;
}

function createEmployee(firstName, lastName, gender, title) {
    const person = createPerson(firstName, lastName, gender);
    const employee = Object.create(person); // <=== The key bit
    Object.defineProperty(employee, "title", {
        get() {
            return title;
        },
        set(value) {
            title = value;
        },
    });
    return employee;
}

const e = createEmployee("Abc", "efg", "Tree", "CEO");

console.log(e.hasOwnProperty("firstname")); // false
console.log(e.hasOwnProperty("title"));     // true

e.firstname = "Joe";
console.log(e.hasOwnProperty("firstname")); // false

Both styles of using JavaScript (and several others) are perfectly valid. Using constructor functions is very common (and better supported these days via class syntax), but it's not the only way to use JavaScript.

Upvotes: 2

Related Questions