patternjavascriptModerate
Follow up - Creating Inheritance hierarchy using function constructor
Viewed 0 times
creatinghierarchyfunctioninheritanceconstructorfollowusing
Problem
In continuation with this question,
-
-
-
-
Derived class
Constructor pattern is used amidst construction and modification of objects.
Criteria is,
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
Animal class has four fields:-
name-
sound-
owner-
favFoodCat derived class has one fieldmode
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
Let's, for example, use this code as a starting point (because it's simpler to put into a REPL):
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.
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.
Again, all seems fine, until..
Crap! Massive bug. What's happened here?
In your
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:
Now your code will yell at you before you end up with nasty leakages..
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
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());
> fooThis 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());
> lokiAgain, all seems fine, until..
console.log(animal.getOwner())
> lokiCrap! 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 definedOh, 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());
> foovar thor = Object.create(animalPrototype);
thor.setOwner('loki'); // We all know it is true.
console.log(thor.getOwner());
> lokiconsole.log(animal.getOwner())
> lokiContext
StackExchange Code Review Q#115239, answer score: 10
Revisions (0)
No revisions yet.