alexroat
alexroat

Reputation: 1727

Javascript Inheritance calling parent's non default constructor

I know that classical pattern for inheritance with prototype is based to set the object prototype of the constructor function. However my willing is to have the possibility to call the parent constructor from the derived class constructor with some arguments that are not available before the call.

This is well accomplished in Java/Python/PHP with super(...) or Parent.__init__(...) method.

However in plain Javascript (not coffescript or similar) there is no way to do that ( parent.apply(this,arguments) does but not it does not setup the prototype).

After some readings I've got this solution, adding the "inherits" method to the prototype of Function. This simply add the definition of super to the derived function in order to initialize the prototype according some parameters.

Function.prototype.inherits=function(parent)
{
    var ctor=this;
    var p=Object.create(parent);
    ctor.super=function()
    {
        parent.apply(p,arguments);
    }
    ctor.prototype=p;
}


//testing

function A(x)
{
    this.x=x;
}

function B(y)
{
    B.super(y*2);//Here "super" is available and I can pass parameters. 
    this.y=y;
}
B.inherits(A);//here I define the inheritance


a=new A(3);
b=new B(5);


console.log(a);//returns { x: 3 }
console.log(b);//returns { y: 5 }
console.log(b.x);//returns 10


console.log(a instanceof A);//returns true
console.log(b instanceof B);//returns true

In this way I've got the behaviour that I expected. My question is: what are the drawbacks of this solution? Are there more efficent solutions to the same problem ? Is this solution cross-browser?

PS: I've invented it by myself :)

EDIT: in order to avoid collisions with other libraries I could define a standalone function like this that accomplish the same target.

function inherits(klass,parent)
{
    var p=Object.create(parent);
    klass.super=function()
    {
        parent.apply(p,arguments);
    }
    klass.prototype=p;
}

And in the testing after definition of B simply call

inherits(B,A);

EDIT 2: After Moolamaduck consideration I have rewritten the code in order to solve the problem of shared prototype. The result is pretty simple to use and elegant (IMHO).
https://stackoverflow.com/a/33270707/76081

Upvotes: 0

Views: 142

Answers (4)

alexroat
alexroat

Reputation: 1727

After Moolamaduck consideration I have rewritten the code in order to solve the problem of shared prototype. The result is pretty simple to use and elegant (IMHO).

Here is it, with testing:

function inherits(ctor,parent)
{
    ctor.prototype=Object.create(parent.prototype);
    ctor._super=function()
    {
        parent.apply(this,arguments);
    }
}


//testing

function A(x)
{
    this.x=x;
};

function B(y)
{
    B._super.call(this,y*2);//Here "_super" is available (after calling inherits) and I can pass parameters to the parent constructor.
    this.y=y;
};
inherits(B,A);// Here we call inherits, after this parent will be available in the B constructor


a=new A(3);
b=new B(5);


console.log(a);//returns A {x: 3}
console.log(b);//returns B {x: 10, y: 5}
console.log(b.x);//returns 2*5=10

console.log(a instanceof A);//returns true
console.log(b instanceof B);//returns true

//post instantiation method definition and inheritance
A.prototype.test=function(){console.log(this.x+1);};
a.test();//returns 3+1=4
b.test();//returns 2*5+1=11



var b1 = new B(1);
var b2 = new B(2);


console.log(b1.x);//returns 2
console.log(b2.x);//returns 4

Upvotes: 0

Alvin Pascoe
Alvin Pascoe

Reputation: 1209

It seems like you're looking for a general pattern in which to recreate the classical Object Orientated paradigm in JavaScript (including use of super).

A few years ago John Resig (the creator of jQuery) suggested a set of functions that allows you to mimic this in Javascript.

http://ejohn.org/blog/simple-javascript-inheritance/

However, for a couple of reasons (including use of the arguments.callee property) this in now considered deprecated, but can still act as a good base.

There are a number of improvements that have been suggested which replace the arguments.callee property, including:

Is John Resig's Javascript inheritance snippet deprecated?

https://codereview.stackexchange.com/questions/30018/improving-on-john-resigs-simple-javascript-inheritance-avoiding-new

If you're looking for a reliable way to recreate classical Object Orientation in JavaScript, I would research further into improvements on John's original code (the guy knows what he's talking about and it's a good base to build on).

Upvotes: 1

mooiamaduck
mooiamaduck

Reputation: 2156

Here's a minimal example which achieves what you want (calling a parent constructor from within the constructor of a derived class):

var Shape = function(sides) {
  this.sides = sides;
};

var Square = function(size) {
  /* Just call the parent constructor function with `this` as context. */
  Shape.call(this, 4);
  this.size = size;
};

/* Set up the prototype chain. Use a shim for `Object.create` if you want. */
Square.prototype = Object.create(Shape.prototype);

That's all there is to it: call the parent constructor with the object being constructed as context, and set up the prototype chain.

There is one serious flaw in the code you posted. Namely, you are calling the parent constructor with the prototype of the derived class as context, rather than the object being constructed. This means that members which the parent constructor initializes will be updated on the prototype of all instances of the derived class, and so all instances will be updated. This is not what you want.

To illustrate the problem:

Function.prototype.inherits=function(parent)
{
    var ctor=this;
    var p=Object.create(parent);
    ctor.super=function()
    {
        parent.apply(p,arguments);
    }
    ctor.prototype=p;
}


function A(x)
{
    this.x=x;
}

function B(y)
{
    B.super(y*2);
    this.y=y;
}

B.inherits(A);

var b1 = new B(1);
var b2 = new B(2);
alert(b1.x); // displays "4" instead of "2"!

Upvotes: 1

Rick Viscomi
Rick Viscomi

Reputation: 8852

One drawback could be that extending prototype of built-in objects like Function or Array has the possibility to clash with third party scripts that run on the page. If two scripts try to overwrite the same prototype, you can get unexpected results.

In terms of browser compatibility, it appears that the weakest link is Object.create, which is not supported in IE 8 and older. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Browser_compatibility

Upvotes: 1

Related Questions