1

I have a constructor like this in JavaScript:

var BaseThing = function() {
  this.id = generateGuid();
}

As you'd expect, when you create a new BaseThing, the ID is unique each time.

var thingOne = new BaseThing();
var thingTwo = new BaseThing();
console.log(thingOne.id === thingTwo.id); // false

But things get hairy when I try to create objects that inherit from BaseThing:

var FancyThing = function() {
   this.fanciness = "considerable";
}
FancyThing.prototype = new BaseThing();

var thingOne = new FancyThing();
var thingTwo = new FancyThing();
console.log(thingOne.id === thingTwo.id); // true

This of course makes sense because of the way prototypical inheritance works, but it's not what I want; I want the ID to be unique without having to reimplement it on each object that inherits from BaseThing.

What's the best way to do this? The only solutions I could come up with myself were to (a) reimplement the id on each child constructor (but this seems to defeat the point of inheritance) or (b) add some sort of initialize function to BaseThing (but I don't want to have to worry about making sure it's called every time a Thing is created).

chsttr
  • 21
  • 2
  • This really boils down to "how to ensure a child calls a parent constructor". The answer is that you can't ensure it, other than having each child actually call the parent within its own constructor. That at least puts the onus on the child class to do it, rather than the consumer of the child class. – James Thorpe Jun 06 '16 at 14:55
  • how `generateGuid()` works? Does it know the type of object the id is being created for? maybe you can pass a parameter depending on the type of object and generate specific id... – DIEGO CARRASCAL Jun 06 '16 at 14:57
  • I guess I was basically hoping there is a more elegant answer than "remember to call the parent constructor every time you do this". – chsttr Jun 06 '16 at 14:59
  • 1
    Do "proper" inheritance: https://stackoverflow.com/questions/17392857/benefits-of-using-object-create-for-inheritance/17393153#17393153 – Felix Kling Jun 06 '16 at 15:05

3 Answers3

3

The problem is that your child doesn't inherit the constructor (function body) from the parent. You can apply the parent function first which would give the desired effect without having to re-write everything contained in the parent. This can be done by using .apply.

var counter = 0;
function generateGuid() { return ++counter; }

var BaseThing = function() {
  this.id = generateGuid();
}

var thingOne = new BaseThing();
var thingTwo = new BaseThing();
console.log(thingOne.id === thingTwo.id); // false


var FancyThing = function() {
  BaseThing.apply(this, arguments) // inherit
  this.fanciness = "considerable";
}
FancyThing.prototype = Object.create(BaseThing.prototype, {constructor: {value: FancyThing, writable: true, configurable: true}});

var thingOne = new FancyThing();
var thingTwo = new FancyThing();
console.log(thingOne.id === thingTwo.id); // false

Unfortunately i'm not aware of a way of extending from a parent without having to in some manner define that the parent is called.

Joey Ciechanowicz
  • 3,345
  • 3
  • 24
  • 48
  • 1
    `FancyThing.prototype = new BaseThing();` should really become `FancyThing.prototype = Object.create(BaseThing.prototype, {constructor: {value: FancyThing, writable: true, configurable: true}});`. – Felix Kling Jun 06 '16 at 15:06
2

Yea, doing the equivalent of calling super wouldn't be a bad way to go. You can use call in addition to apply:

function generateGuid(){
    return Math.random(); 
}

var BaseThing = function() {
  this.id = generateGuid();
}

var FancyThing = function() {
   BaseThing.call(this);
   this.fanciness = "considerable";
}


var o = new FancyThing();
console.log(o);

https://repl.it/CYp1/0

Robert Moskal
  • 21,737
  • 8
  • 62
  • 86
2

You can go fancy by moving id property to BaseThing.prototype and making it "computed" using Object.defineProperty

var BaseThing = function() {
  //will define id on prototype
  //this.id = generateGuid();
}

Object.defineProperty(BaseThing.prototype, 'id', {
  configurable: true,
  enumerable: true,
  get: function() {
    //redefine property on first call
    Object.defineProperty(this, 'id', {
        configurable: false,
        enumerable: true,
        value: generateGuid()
    })

    return this.id
  }
})

Demo.

Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98