Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript: How to extend class properly

Searching over the internet I'm always bumping on this approach of Javascript classes extension

function extend(Child, Parent) {
    var F = function() { }
    F.prototype = Parent.prototype
    Child.prototype = new F()
    Child.prototype.constructor = Child
    Child.superclass = Parent.prototype
}

But how is that different from this one?

  function extend(Child, Parent) {
    var p = new Parent()
    Child.prototype = p
    Child.prototype.constructor = Child
    Child.superclass = p
  }

This last one also works perfect. So why should I use this extra var F = function() { } move then?

like image 381
pehas Avatar asked Dec 12 '13 21:12

pehas


2 Answers

Invoking the original constructor directly can have undesirable side effects, like not working properly if certain expected arguments are not passed.

That's why they use a "proxy" function, which lets you get a new object that inherits from Parent() without actually invoking Parent().


Here's a simple example:

function Person(name, age) {
    if (name === undefined)
        throw "A name is required";
    this.name = name + "";
    this.age = age;
}

If Person is the parent, it'll throw an error because there was no name passed.

like image 106
cookie monster Avatar answered Oct 26 '22 11:10

cookie monster


The first example is (as cookie monster mentioned in the comment) a shim for the following piece of code which might be easier to understand.:

function extend(Child, Parent) {
  Child.prototype = Object.create(Parent.prototype);
  Child.prototype.constructor = Child;
  Child.superclass = Parent.prototype;
}

Basically, this implementation makes the object that all Child instances inherit from (Child.prototype) inherit from the object that all Parent instances inherit from (Parent.prototype). Intuitively this is the most accurate representation of class inheritance JavaScript provides.

The second implementation of extends is flawed, because all Child instances will inherit from a specific Parent instance. Should there be significant differences between Parent instances (due to the parameters passed to the constructor for example), the Child instances can not accurate represent that, because they all inherit from a Parent instance created by calling the Parent constructor with no arguments.

Here is an example of what the first implementation can do and the second one can not:

function Parent(name, age) {
  this.name = name;
  this.age = age;
}
Parent.prototype.greet = function() { return 'I am parent ' + this.name; }

function Child(name){
  Parent.call(this, name, 20); // notice the call to the superclass
}
extend(Child, Parent);
Parent.prototype.greet = function() { return 'I am child ' + this.name + ' and i\'m ' + this.age; }

var c = new Child('Tom');
console.log(c.greet()); // I am child Tom and i'm 20

As a sidenote, in the Child constructor i have called the Parent constructor. This is actually quite common when dealing with classical inheritance, so that's another point for the first implementation. It isn't actually required, the Child constructor can safely ignore calling the Parent constructor, but keep in mind that that call basically ensures that the new object created is a valid Parent instance before being a child Instance. In my example if you were to not call the Parent constructor, the name and age properties would not be set on the Child instance, so the greet method would return I am child undefined and i'm undefined, far from what you would expect.

like image 44
Tibos Avatar answered Oct 26 '22 11:10

Tibos