HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptModerate

Follow up - Creating Inheritance hierarchy using function constructor

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
creatinghierarchyfunctioninheritanceconstructorfollowusing

Problem

In continuation with this question,


Animal class has four fields:



-
name

-
sound

-
owner

-
favFood



Cat derived class has one field



  • mode





Derived class Cat is created such that every instance of Cat inherits instance members of Animal. In addition, Cat adds a new instance member mode.


Constructor pattern is used amidst construction and modification of objects.


Criteria is, Animal and Cat class should enforce encapsulation and proper inheritance.

Solution



`/ example.js /
function Animal() {
var _name = "";
var _sound = "";
var _owner = "";
var _favFood = "";

Object.defineProperties(this, {
'name': {
get: function () {
return _name;
},
set: function (name) {
//check conditions before setting the name
_name = name;
}
},
'owner': {
get: function () {
return _owner;
},
set: function (ownerName) {
//check conditions before setting the owner
_owner = ownerName;
}
},
'sound': {
get: function () {
return _sound;
},
set: function (sound) {
//check conditions before setting the sound
_sound = sound;
}
},
'favFood': {
get: function () {
return _favFood;
},
set: function (favFood) {
//check conditions before setting the name favFood
_favFood = favFood;
}
}
});
}

//Below code is the interface for Animal and its subclass type objects
Animal.prototype.getName = function(){ return name;} // calls get property of name
Animal.prototype.setName = function(newName){name = newName;} //calls set property of nam

Solution

200's answer is more or less on point and covers things from an idiomatic point of view, so I'd like to just address a bug in your code. It's actually a fairly major one:

Your getXXX/setXXX methods are going to break your code because they reference globals. We'll use this one as an example:

Animal.prototype.getOwner   =   function(){ return owner;}
Animal.prototype.setOwner   =   function(newOwner){owner = newOwner;}


Let's, for example, use this code as a starting point (because it's simpler to put into a REPL):

var animalPrototype = {
  getOwner: function() {
    return owner;
  },
  setOwner: function(newOwner) {
    owner = newOwner;
  }
};
var animal = Object.create(animalPrototype);


This code does practically the same thing you've got above, just without the class nonsense. I'm going to execute all of this in a REPL first of all with strict mode off.

animal.setOwner('foo');
console.log(animal.getOwner());
> foo


This seems good so far. Now, let's create another animal. I'm going to give it a name - Thor. Because that was the name of my dog, and I miss him. Also, because Thor invokes the thought of fury and lightning, which is exactly what the V8 engine is about to do to me.

var thor = Object.create(animalPrototype);
thor.setOwner('loki'); // We all know it is true.
console.log(thor.getOwner());
> loki


Again, all seems fine, until..

console.log(animal.getOwner())
> loki


Crap! Massive bug. What's happened here?

In your getXXX/setXXX methods you're assigning and returning a global. Now, if this was executing in strict mode this should procure a ReferenceError. But because this is not being executed in strict mode, the browser sort of goes with it and in order to make the code work, it will simply assume that when you say owner, you actually mean window.owner (it's quite a bit more complicated than this, but this is the nutshell version).

Congratulations! You've just created a stateful global variable, which is one of the worst evils in the world, right up there next to kicking puppies and depriving people of Fallout 4.

This 'quirk' exists for (unfortunate) legacy reasons with JavaScript and is one of the reasons why we enable strict mode. Enabling strict mode is super simple. The above code sample looks like this:

'use strict';
var animalPrototype = {
  getOwner: function() {
    return owner;
  },
  setOwner: function(newOwner) {
    owner = newOwner;
  }
};
var animal = Object.create(animalPrototype);


Now your code will yell at you before you end up with nasty leakages..

animal.getOwner()
> Uncaught ReferenceError: owner is not defined
animal.setOwner('foo')
> Uncaught ReferenceError: owner is not defined


Oh, and be careful when using strict mode. You don't want to mix strict code with non-strict code, so if you aren't using a bundler like webpack, you'll want to wrap your entire code in an IIFE and then use the 'use strict' directive in there instead. This prevents code that relies on that weird quirk from going bananas. Example:

(function() {
  'use strict';
  var animalPrototype = {
    getOwner: function() { return this.owner; },
    setOwner: function(newOwner) { this.owner = newOwner; }
  }
  var animal = Object.create(animalPrototype);
}());

Code Snippets

Animal.prototype.getOwner   =   function(){ return owner;}
Animal.prototype.setOwner   =   function(newOwner){owner = newOwner;}
var animalPrototype = {
  getOwner: function() {
    return owner;
  },
  setOwner: function(newOwner) {
    owner = newOwner;
  }
};
var animal = Object.create(animalPrototype);
animal.setOwner('foo');
console.log(animal.getOwner());
> foo
var thor = Object.create(animalPrototype);
thor.setOwner('loki'); // We all know it is true.
console.log(thor.getOwner());
> loki
console.log(animal.getOwner())
> loki

Context

StackExchange Code Review Q#115239, answer score: 10

Revisions (0)

No revisions yet.