1

Is this a good/safe cross-browser way to implement method overriding in JavaScript? :

function Person(firstName, lastName)
{
  this.firstName = firstName;
  this.lastName = lastName;
};

Person.prototype.sayHi = function()
{
  return "Hi, my name is " + this.firstName;
};

function Employee(firstName, lastName, position)
{
  Person.call(this, firstName, lastName);
  this.position = position;
};

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

Employee.prototype.sayHi = function()
{
  return this.constructor.prototype.sayHi() + " I'm a " + this.position;
}

I could also write:

Employee.prototype.sayHi = function()
{
  return Person.prototype.sayHi() + " I'm a " + this.position;
}

but with this solution I'm refering to direct method, or

Employee.prototype.sayHi = function()
{
  return this.__proto__.sayHi() + " I'm a " + this.position;
}

but this one is awful and not crossbrowser.

EDIT: I assumed Object.create is crossbrowser as I can use a shim

dragonfly
  • 17,407
  • 30
  • 110
  • 219

2 Answers2

1

No.

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

Object.create is not supported by older browsers, but you can shim it.

Employee.prototype.sayHi = function() {
    return this.constructor.prototype.sayHi() + " I'm a " + this.position;
}

I wouldn't use that. You expect this.constructor to be the Person from which you inherited from, but that might not be the case. Actually it would be good practise to set the Employee.prototype.constructor = Employee, which would cause recursion with a stack overflow to your method.

Instead, you should explicitly refer to the function you want to use:

return Person.prototype.sayHi.call(this) + " I'm a " + this.position;

If you want it dynamically, you'd use

return Object.getPrototypeOf(Employee.prototype).sayHi.call(this) + " I'm a " + this.position;

but notice that Object.getPrototypeOf is not supported in older browsers. So if you need a cross-browser and DRY way, use the module pattern:

(function(proto, super) {
     proto.sayHi = function() {
         return super.sayHi.call(this) + " I'm a " + this.position;
     };
     …
})(Employee.prototype, Person.prototype);

or the revealing prototype pattern:

Employee.prototype = (function(super) {
     var proto = Object.create(super);
     proto.sayHi = function() {
         return super.sayHi.call(this) + " I'm a " + this.position;
     };
     …
     return proto;
})(Person.prototype);
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

You should not rely on the constructor property of the prototype to call parent functions because normally, that property should point to the children constructor.

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

There are a few other ways to access parent functions safely, like referring to the parent prototype directly like you are trying to do in your second example, however there's something wrong with the way you are doing it.

If you call sayHi without using call or apply, this will point to Person.prototype during the function call and that's not what you want. You should call sayHi by setting the context object to the current employee instance, like this:

Employee.prototype.sayHi = function() {
  return Person.prototype.sayHi.call(this) + " I'm a " + this.position;
};

Another way of doing this would be to store a reference to the parent prototype in the children prototype.

 Employee.prototype.super = Person.prototoype;

 Employee.prototype.sayHi = function() {
    return this.super.sayHi.call(this) + " I'm a " + this.position;
 };

Another interesting approach I've seen is to wrap every function of the children prototype in a function that dynamically sets a reference to the parent function on the object instance, so that you can only use this.parent() to call the function. However I would not recommend this solution since it will reduce performances and increase memory usage.

I have created a jsFiddle to demonstrate that concept.

plalx
  • 42,889
  • 6
  • 74
  • 90