1

I have spent the past few weeks doing a ton of research on Javascript inheritance. Firstly, I want to say that I am NOT trying to implement class-based inheritance in Javascript, however, I am trying to mimic inheritance CORRECTLY using Javascript's prototypal nature. This may be beating a dead horse, but here goes my question:

I have two simple functions:

function Animal(arg) {
    var a = arg;

    //privileged function has access to private variable
    this.sayHi = function(name) {
        // can access a here if needed
        console.log('Hi: ' + name);
    }
}

and:

function Person(arg) {
    this.sayHi = function(name) {
        super.sayHi(name + '!!!');
    };
}

Person.inheritsFrom(Animal);

The following would be the expected result:

var animal = new Animal('cow');
animal.sayHi('John'); // Hi: John

var person = new Person('adult');
person.sayHi('Fred'); // Hi: Fred!!!

The "inheritsFrom()" method that I'm using for inheritance looks like:

Function.prototype.inheritsFrom = function(parent) {
    if(parent.constructor == Function) { //Normal Inheritance
        this.prototype = new parent;
        this.prototype.constructor = this;
        this.prototype.parent = parent.prototype;
    } else  { //Pure Virtual Inheritance
        this.prototype = parent;
        this.prototype.constructor = this;
        this.prototype.parent = parent;
    }
    return this;
}

These are some of my references: http://phrogz.net/JS/classes/OOPinJS2.html http://www.crockford.com/javascript/inheritance.html https://medium.com/javascript-scene/common-misconceptions-about-inheritance-in-javascript-d5d9bab29b0a

Lots of good info, still can't figure out how to inherit "privileged" methods as shown above. I'm able to call the privileged method of the parent class. Please see the following JS Fiddle: https://jsfiddle.net/jxjk5hm9/

Any guidance on inheriting privileged methods is greatly appreciated, thanks!

Adam Jenkins
  • 51,445
  • 11
  • 72
  • 100
ossys
  • 4,157
  • 5
  • 32
  • 35
  • Yeah, you're beating a dead horse. This is JavaScript - until ES7 becomes the standard, you have to use it the way it is currently designed to be implemented - using prototypal inheritance and not classical inheritance. – Adam Jenkins Jul 30 '15 at 00:28

1 Answers1

2

There is no pretty solution to this. First off,

 this.prototype = new parent;

is not a good way to establish inheritance. Rather use

 this.prototype = Object.create(parent.prototype);

See Benefits of using `Object.create` for inheritance for more info.

Inside the child constructor, you'd have to call the parent constructor, applying it to the current instance:

function Person(arg) {
    Animal.call(this, arg);
}

then you have to iterate over all methods that have been attached to this by Animal and keep a reference to them:

function Person(arg) {
    Animal.call(this, arg);
    var privilegedSuper = Object.keys(this)
      .filter(function(prop) { return typeof this[prop] === 'function'; }.bind(this))
      .reduce(function(obj, prop) { return (obj[prop] = this[prop]), obj; }.bind(this));
}

which you can then reference in the overridden methods:

function Person(arg) {
    Animal.call(this, arg);
    var privilegedSuper = Object.keys(this)
      .filter(function(prop) { return typeof this[prop] === 'function'; }.bind(this))
      .reduce(function(obj, prop) { return (obj[prop] = this[prop]), obj; }.bind(this));

    this.sayHi = function(name) {
      privilegedSuper.sayHi.call(this, name + '!!!');
    };
}

Is it worth the effort and the complexity? I don't think so.

Function.prototype.inheritsFrom = function(parent) {
  if (parent.constructor == Function) { //Normal Inheritance
    this.prototype = Object.create(parent.prototype);
    this.prototype.constructor = this;
    this.prototype.parent = parent.prototype;
  } else { //Pure Virtual Inheritance
    this.prototype = parent;
    this.prototype.constructor = this;
    this.prototype.parent = parent;
  }
  return this;
}

function Animal(arg) {
  var a = arg;

  //privileged function has access to private variable
  this.sayHi = function(name) {
    // can access a here if needed
    console.log('Hi: ' + name + ' (and here is a: ' + a + ')');
  }
}

function Person(arg) {
  Animal.call(this, arg);
  var privilegedSuper = Object.keys(this)
    .filter(function(prop) {
      return typeof this[prop] === 'function';
    }.bind(this))
    .reduce(function(obj, prop) {
      return (obj[prop] = this[prop]), obj;
    }.bind(this), {});

  this.sayHi = function(name) {
    privilegedSuper.sayHi.call(this, name + '!!!');
  };
}
Person.inheritsFrom(Animal);

var animal = new Animal('cow');
animal.sayHi('John'); // Hi: John

var person = new Person('adult');
person.sayHi('Fred'); // Hi: Fred!!!
Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 1
    Very clever. Just noting that `.filter(function(prop) {...}.bind(this))` could be `.filter(function(prop) {...}, this)`, which is likely more efficient, but I guess it keeps the style consistent with the use of *reduce*, which doesn't have a *thisArg* parameter. – RobG Jul 30 '15 at 01:05
  • @RobG: Ideally I would use an arrow function ;) – Felix Kling Jul 30 '15 at 01:23
  • Felix, excellent response, thank you! It's funny because I started looking at that exact implementation of cycling through the privileged methods and attaching them to a variable... but then I thought I was being crazy and there must be a better way which is why I posted on SO. I'm marking this as the accepted answer due to the clarity of the explanation and the code example, however I think I'm moving away from privileged methods and instead attaching everything to the object prototype instead... Just will need to make all private variables public so they can be accessed by the prototype. – ossys Jul 30 '15 at 15:48
  • @ossys: Yes, that would be the best solution IMO. I'm not an advocate of "privileged methods" in JS. It just introduces unnecessary complexity IMO. – Felix Kling Jul 30 '15 at 16:01