0

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

10
  • One drawback is that you're modifying a built-in object, this is generally considered (for good reasons) to be bad practice. Modifying the prototypes of Function/Array/Object etc. is a bad (or at least fragile) idea. In terms of efficiency, if your object creation is your bottleneck you are almost certainly doing it wrong. Cross browser? Unless you need to support old IE you're fine. Commented Oct 19, 2015 at 23:59
  • Efficiency in terms of what metric? Run time? Code complexity/maintainability? Commented Oct 20, 2015 at 0:00
  • Also, is there some huge objection you have against transpilers? Coffeescript, babel, and traceur all handle super just fine. Or is this more for an educational purpose? Commented Oct 20, 2015 at 0:06
  • Yep, efficency in therms of speed and memory. Commented Oct 20, 2015 at 0:08
  • 1
    It is trivially correct with no important flaws other than incompleteness. How well does it handle subclassing Array for instance? How bout Date? RegExp? If all you want is to subclass your own user-defined constructor functions then I think you will find your version easy to maintain and plenty performant with the caveats discussed above. Commented Oct 20, 2015 at 0:22

4 Answers 4

1

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"!

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! I've got the problem and fixed the code... see the last answer: stackoverflow.com/a/33270707/76081
1

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

5 Comments

Object.create is trivial to polyfill.
That doesn't mean it is. I was referring to the OP's code in a vacuum.
Thank you. Who cares anymore about IE8 :) About the clash of third party script see the edit
I personally don't, but considering IE 8 has greater market share than IE 9 and 10, it's still something to consider. Source: gs.statcounter.com/…
You're right ... probably there is a way to modify it and make it work with IE8... Targeting the mobile and the recent browsers I think IE8 could be considered outdated for the modern webapps.
1

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).

2 Comments

Yep. This is exactly what I want to do. Thanks for the references.
@alexroat - No problem, I've looked into similar things myself!
0

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

1 Comment

You can also write ctor._super=function() { ... } as ctor._super = parent;.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.