33

Let's say I have the following code snippet.

function test(id) { alert(id); }

testChild.prototype = new test();

function testChild(){}

var instance = new testChild('hi');

Is it possible to get alert('hi')? I get undefined now.

GSerg
  • 76,472
  • 17
  • 159
  • 346
Moon
  • 22,195
  • 68
  • 188
  • 269

5 Answers5

105

JS OOP ...

// parent class
var Test = function(id) {
    console.log(id);
};

// child class
var TestChild = function(id) {
    Test.call(this, id); // call parent constructor
};

// extend from parent class prototype
TestChild.prototype = Object.create(Test.prototype); // keeps the proto clean
TestChild.prototype.constructor = TestChild; // repair the inherited constructor

// end-use
var instance = new TestChild('foo');
roylaurie
  • 1,838
  • 2
  • 14
  • 8
  • 1
    Thanks, definitely the way to do this in JS :P – Kenny Cason Jul 09 '13 at 09:58
  • 1
    You get messed up prototype chains doing it this way. The way to do this is actually more like http://js-bits.blogspot.com.au/2010/08/javascript-inheritance-done-right.html – papercowboy Jul 24 '13 at 14:39
  • @cayuu How does this mess up the prototype chain? – roylaurie Oct 18 '13 at 15:15
  • 2
    Apologies, "messed up" is harsh - it makes _dirty_ proto chains, assigning all parent instance properties as prototype properties (which may not be what you intend), but the technique works if you don't care about that - ex: http://imgur.com/NBDA5ob (clean inheritance would leave 'id' as an instance property instantiated by the constructor, not as a prototype entity). – papercowboy Oct 19 '13 at 03:45
  • 1
    @cayuu I've come to see exactly what you mean nowadays. I've switched to Object.create() as it doesn't use instantiation. The second param of Object.create() is pretty cool too - it creates real constants. – roylaurie Dec 12 '13 at 07:21
  • `Object prototype may only be an Object or null` — node – Lee Goddard Oct 13 '15 at 12:59
  • Clearest answer ever on prototypes with constructor functions, thanks – Alexander Derck Nov 16 '16 at 14:20
28

You already have many answers, but I'll throw in the ES6 way, which IMHO is the new standard way to do this.

class Parent { 
  constructor() { alert('hi'); } 
}
class Child extends Parent { 
  // Optionally include a constructor definition here. Leaving it 
  // out means the parent constructor is automatically invoked.
  constructor() {
    // imagine doing some custom stuff for this derived class
    super();  // explicitly call parent constructor.
  }
}

// Instantiate one:
var foo = new Child();  // alert: hi
Sk606
  • 2,182
  • 1
  • 21
  • 14
  • Good point. Unfortunately, `super()` is not supported even by IE11 (http://kangax.github.io/compat-table/es6/), which could be an issue in productive environments. – BurninLeo Jul 23 '17 at 18:36
  • 3
    Good reminder As usual, when possible it's a good idea to transpile your source to ES5 for this and other incompatibilities. You can do this using tools like Webpack, Typescript, etc. – Sk606 Aug 08 '17 at 19:52
5

That's how you do this in CoffeeScript:

class Test
  constructor: (id) -> alert(id)

class TestChild extends Test

instance = new TestChild('hi')

Nope, I'm not starting a holy war. Instead, I'm suggesting to take a look at resulting JavaScript code to see how subclassing could be implemented:

// Function that does subclassing
var __extends = function(child, parent) {
  for (var key in parent) {
    if (Object.prototype.hasOwnProperty.call(parent, key)) {
      child[key] = parent[key];
    }
  }
  function ctor() { this.constructor = child; }
  ctor.prototype = parent.prototype;
  child.prototype = new ctor;
  child.__super__ = parent.prototype;
  return child;
};

// Our code
var Test, TestChild, instance;

Test = function(id) { alert(id); };

TestChild = function() {
  TestChild.__super__.constructor.apply(this, arguments);
}; __extends(TestChild, Test);

instance = new TestChild('hi');

// And we get an alert

See it in action at http://jsfiddle.net/NGLMW/3/.

To stay correct, the code is slightly modified and commented to be more readable, compared to CoffeeScript output.

Anton Strogonoff
  • 32,294
  • 8
  • 53
  • 61
3

By taking advantage of variable arguments and the apply() method, you could do it this way. Here's a fiddle for this example.

function test(id) { alert(id); }
function testChild() {
  testChild.prototype.apply(this, arguments);
  alert('also doing my own stuff');
}
testChild.prototype = test;
var instance = new testChild('hi', 'unused', 'optional', 'args');
JCotton
  • 11,650
  • 5
  • 53
  • 59
  • That's probably the most cross-browser solution which takes advantage of well defined language standards. – Bruno Finger Apr 27 '17 at 19:07
  • Also note that this can probably be rewritten as `this.__proto__.apply(this, arguments)` so it's more generic and reusable. And also note this won't work if you define your inheritance using `Object.create` as in `testChild.prototype = Object.create(test.prototype);` which seems to be a fairly accepted way to create inheritance in the wild. – Bruno Finger Apr 27 '17 at 19:13
1

You need to declare the function testChild() before you set its prototype. Then you need to call testChild.test to call the method. I believe you want to set testChild.prototype.test = test, then you can call testChild.test('hi') and it should resolve properly.

Paul Sonier
  • 38,903
  • 3
  • 77
  • 117
  • // yes...I can manually call test, but what I want to do is call test() without invoking it like we can in other OOP languages. – Moon Jul 07 '11 at 22:37