accessing static (class) variables defined in a subclass from a superclass - javascript

I have some classes that share identical methods and are distinguished only by a few static (aka class) variables. My thought is to put the common methods into a base class that accesses the static variables.
Here is a solution that works, but it seems like a real cough kludge. Is there a better / more idiomatic way to do this?
"use strict";
// common code
function Base() { }
Base.prototype.f1 = function() {
console.log(Object.getPrototypeOf(this).constructor.VAR1); // this feels really really wrong!
}
Base.prototype.f2 = function() {
console.log(Object.getPrototypeOf(this).constructor.VAR2); // ditto
}
// specialization A
function SubA() { Base.call(this); }
SubA.prototype = Object.create(Base.prototype);
SubA.prototype.constructor = SubA;
SubA.VAR1 = "suba v1";
SubA.VAR2 = "suba v2";
// specialization B
function SubB() { Base.call(this); }
SubB.prototype = Object.create(Base.prototype);
SubB.prototype.constructor = SubB;
SubB.VAR1 = "subb v1";
SubB.VAR2 = "subb v2";
This works as expected:
> var a = new SubA();
> var b = new SubB();
> a.f1()
suba v1
undefined
> b.f2()
subb v2
undefined
an alternative
Of course I could write methods to encapsulate the differences between SubA and SubB. The syntax is less tortured, but it still feels wrong to write methods that are essentially behaving like static variables:
"use strict";
function Base() { }
Base.prototype.f1 = function() {
console.log(this.getVar1());
}
Base.prototype.f2 = function() {
console.log(this.getVar2());
}
function SubA() { Base.call(this); }
SubA.prototype = Object.create(Base.prototype);
SubA.prototype.constructor = SubA;
SubA.prototype.getVar1 = function() { return 'suba v1'; }
SubA.prototype.getVar2 = function() { return 'suba v2'; }
function SubB() { Base.call(this); }
SubB.prototype = Object.create(Base.prototype);
SubB.prototype.constructor = SubB;
SubB.prototype.getVar1 = function() { return 'subb v1'; }
SubB.prototype.getVar2 = function() { return 'subb v2'; }
> var a = new SubA();
> var b = new SubB();
> a.f1()
suba v1
undefined
> b.f2()
subb v2
undefined

Is there a particular reason to put VAR1 and VAR2 in the class objects themselves rather than in their prototypes? If not, things become much simpler:
function Base() { }
Base.prototype.f1 = function() {
console.log(this.VAR1);
};
Base.prototype.f2 = function() {
console.log(this.VAR2);
};
// specialization A
function SubA() { Base.call(this); }
SubA.prototype = Object.create(Base.prototype);
SubA.prototype.constructor = SubA;
SubA.prototype.VAR1 = "suba v1";
SubA.prototype.VAR2 = "suba v2";
// specialization B
function SubB() { Base.call(this); }
SubB.prototype = Object.create(Base.prototype);
SubB.prototype.constructor = SubB;
SubB.prototype.VAR1 = "subb v1";
SubB.prototype.VAR2 = "subb v2";
The above code passes your tests.

Related

Passing references of a class to another class and using its methods

Let's say you're making a game. You want to try and not pollute the global scope and possibly limit the user's ability to easily alter the game (doubtful with client-side). You feel like modules might be unnecessary for your purposes. Is it bad practice to pass references to a class to another class during instantiation to access its methods?
Contrived example:
//game.js
var Game = (function () {
function Game() {
this.currentLevel = null;
this.score = 0;
}
Game.prototype.addScore = function (num) {
this.score += num;
};
Game.prototype.goToLevel = function (diff) {
this.currentLevel = new Level(this, diff);
};
Game.prototype.returnHome = function (level) {
this.currentLevel = null;
};
return Game;
})();
//level.js
var Level = (function () {
function Level(game, difficulty) {
this.game = game; //reference to game
this.difficulty = difficulty;
this.entities = [];
this.load();
}
Level.prototype.load = function () {
this.addEntity({name: 'tim', power: 23, difficulty: this.difficulty});
};
Level.prototype.leave = function () {
this.game.returnHome();
};
Level.prototype.addEntity = function (options) {
this.entities.push(new Entity(this, options));
};
Level.prototype.removeEntity = function (entity) {
for(var x = 0; x < this.entities.length; x++) {
if(this.entities[x] === entity) this.entities.splice(x, 1);
}
};
return Level;
})();
//level.js
var Entity = (function () {
function Entity(level, options) {
this.level = level; //reference to level
this.options = options;
}
Entity.prototype.kill = function () {
this.level.removeEntity(this); // anti-pattern?
this.level.game.addScore(34.53); // too closely coupled?
};
return Entity;
})();
//main.js
var Main;
(function (Main) {
var game = null;
function documentIsReady() {
start(); // Start the game
}
function start() {
game = new Game();
game.goToLevel('hard');
}
return {
documentIsReady: documentIsReady
}
})(Main || (Main = {}));
$(document).ready(function () {
Main.documentIsReady();
});
Forgive the half-baked example. If you end up with many instances of the 'Entity' class, do all the references to 'Level', though the same instance, start taking more memory? Are there other pitfalls? Another method would be to implement some kind of interface that you can access that allow classes to talk to each other.

JavaScript inheritance: member functions not inheriting?

This is driving me crazy. I'm about to break down and cry.
Here's my code that is NOT working:
// parent class: Shirt
var Shirt = function() {
this.basePrice = 1;
}
Shirt.prototype.getPrice = function(){return this.basePrice};
Shirt.prototype.display = function(){
$('ul#products').append('<li>Product: $' + this.getPrice() + '.00</li>');
};
// subclass: ExpensiveShirt inherits from Shirt
var ExpensiveShirt = function() {
this.basePrice = 5;
};
ExpensiveShirt.prototype = Object.create(Shirt);
// make some objects and test them
var s = new Shirt();
s.display(); // this works
console.log(s.getPrice()); // this works
var e = new ExpensiveShirt();
e.display(); // this does not work!
console.log(e.getPrice()); // does not work
HERE IS THE JSFIDDLE
Now, if I add these lines, then it works:
ExpensiveShirt.prototype.getPrice = Shirt.prototype.getPrice;
ExpensiveShirt.prototype.display = Shirt.prototype.display;
But according to this I shouldn't have to: JavaScript inheritance with Object.create()?
And I really don't want to because that is bad programming. >:(
Object.create expects the prototype for the new object as its argument, not the constructor. Change your line to this, and it will work:
ExpensiveShirt.prototype = Object.create(Shirt.prototype);
As #Paulpro mentions, you need to use Object.create on Shirt.prototype and not Shirt for inheritance to work.
I usually use the following two functions to make my life easier when dealing with inheritance in JavaScript:
var Shirt = defclass({
constructor: function () {
this.basePrice = 1;
},
getPrice: function () {
return this.basePrice;
},
display: function () {
alert("Product: $" + this.getPrice() + ".00");
}
});
var ExpensiveShirt = extend(Shirt, {
constructor: function () {
this.basePrice = 5;
}
});
var s = new Shirt;
var e = new ExpensiveShirt;
s.display();
e.display();
console.log(s.getPrice());
console.log(e.getPrice());
<script>
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, properties) {
var prototype = Object.create(constructor.prototype);
for (var key in properties) prototype[key] = properties[key];
return defclass(prototype);
}
</script>
Hope that helps.

How to implement inheritance in Screeps objects?

I've been toying around with Screeps for a while now and last night I decided to work out some of my behaviors into a class hierarchy by deriving two classes, Miner and Transporter from a Creep main class. However, whenever I do
console.log(_.functions(minerInstance));
I get the exact same function list as when I do
console.log(_.functions(transporterInstance));
Could someone tell me if I'm doing something wrong OR if I'm actually running into a limitation of the environment my code runs in?
This is my code:
////////////////////////////
// Creep.js
var Creep = function(creep, room) {
this.creep = creep;
this.room = room;
this.name = creep.name;
this.id = creep.id;
};
module.exports = Creep;
Creep.prototype = {
tick: function() {
console.log("Base class implementation of tick(), should never happen.");
},
getRole: function() {
return this.creep.memory.role;
}
};
////////////////////////////
// Miner.js
var Creep = require("Creep");
var Miner = function(creep, room) {
this.base = Creep;
this.base(creep, room);
//Creep.call(this, creep, room);
};
module.exports = Miner;
Miner.prototype = Creep.prototype;
Miner.prototype.tick = function() {
var creep = this.creep;
if (creep.memory.activity === undefined || creep.memory.activity === "") {
var target = creep.pos.findNearest(Game.SOURCES_ACTIVE);
this.mine(creep, target);
}
var act = creep.memory.activity;
if (act == "mine") {
var target = this.getTarget(creep);
if (target !== undefined) {
if (creep.energy < creep.energyCapacity) {
creep.moveTo(target);
creep.harvest(target);
} else {
console.log("Write dump to truck code");
/*var trucks = find.transporterInRange(creep, 1);
if (trucks.length) {
creep.moveTo(trucks[0]);
var amount = trucks[0].energyCapacity - trucks[0].energy;
creep.transferEnergy(trucks[0], amount);
}*/
}
}
}
};
Miner.prototype.mine = function(creep, target) {
creep.memory.target = target.id;
creep.memory.activity = "mine";
};
Miner.prototype.getTarget = function(creep) {
return Game.getObjectById(creep.memory.target);
};
////////////////////////////
// Transporter.js
var Creep = require("Creep");
var Transporter = function(creep, room) {
Creep.call(this, creep, room);
};
module.exports = Transporter;
Transporter.prototype = Creep.prototype;
Transporter.prototype.tick = function() {
var creep = this.creep;
if (creep.energy < creep.energyCapacity) {
var miner = this.room.findByRole(creep, "miner");
console.log(miner);
if (miner !== null) {
//console.log(miner[0].name);
//creep.moveTo(miner);
} else
console.log("no miners found");
} else {
console.log("moving to drop");
//var drop = find.nearestEnergyDropOff(creep);
//creep.moveTo(drop);
//creep.transferEnergy(drop);
}
};
With this line...
Miner.prototype = Creep.prototype;
... you tell JS that both prototypes are actually the same object. Hence any update for Miner.prototype will affect Creep.prototype too.
One possible approach is using Object.create when establishing the link between prototypes. Here goes a simplified example:
function Foo(a) {
this.a = a;
}
Foo.prototype.tick = function() { console.log('Foo ticks'); };
Foo.prototype.tock = function() { console.log('Foo tocks'); };
function Bar(a, b) {
this.base = Foo;
this.base(a);
this.b = b;
}
Bar.prototype = Object.create(Foo.prototype);
// as you inherit all the properties, you'll have to reassign a constructor
Bar.prototype.constructor = Bar;
Bar.prototype.tick = function() { console.log('Bar ticks'); };
var f = new Foo(1);
f.tick(); // Foo ticks
f.tock(); // Foo tocks
console.log(f); // Foo { a=1, ... }
var b = new Bar(1, 2);
b.tick(); // Bar ticks
b.tock(); // Foo tocks
console.log(b); // Bar { a=1, b=2, ... }

Javascript Object-Oriented-Programming

I found a Module pattern in JS:
<script>
var MODULENAME = (function(my, $) {
my.publicVar = "5";
my.publicFn = function() {};
return my;
}(MODULENAME || {}, jQuery));
</script>
However I cannot perform instantiation. Does the module pattern allow for that?
Instantiantion means basically that you'll run a function using new.
So maybe you're looking for this?
var Some = function (param) {
var somePrivateVar = 'private';
this.somePublicVar = 'public';
this.method = function () {
return param;
};
};
var some = new Some('abla');
console.log(some.method());
// some.somePrivateVar === undefined
// some.somePublicVar === 'public'
In your case MODULENAME is an object (object, not a function) with publicVar and publicFn. It's not meant to be instantiated the same way you wouldn't call new jQuery().
Your module object can contain anything. Perhaps you're looking for including a constructor in it:
var MODULENAME = (function(my, $) {
var privateVar = 10;
my.SomeConstructor = function() {
this.publicVar = 5;
}
my.SomeConstructor.prototype.someMethod = function() {};
my.SomeConstructor.prototype.getPrivate = function() { return 10; };
return my;
}(MODULENAME || {}, jQuery));
var instance = new MODULENAME.SomeConstructor();
instance.publicVar; // 5
instance.privateVar; // undefined
instance.getPrivate(); // 10
You can do this also with prototype Inheritance :
var MyClass = function(name)
{
//sharing name within the whole class
this.name = name;
}
MyClass.prototype.getName = function(){
return this.name;//now name is visible to getName method too
}
MyClass.StaticMethod = function()
{
console.log("Im Static");
// and since is not in prototype chain, this.name is not visible
}
var myclass = new MyClass("Carlos");
console.log(myclass.getName())//print "Carlos"
MyClass.StaticMethod()// print "Im Static"
myclass.StaticMethod() // error
Se all this article

Prototypal Namespacing

I have a Constructor function "Animals", that is namespacing some other Constructor functions, "Crocodile" and "Monkey":
var Monkey = function(Animals) {
this.Animals = Animals;
};
Monkey.prototype.feedMe = function() {
this.Animals.feed();
};
var Crocodile = function(Animals) {
this.Animals = Animals;
};
Crocodile.prototype.feedMe = function() {
this.Animals.feed();
};
var Animals = function(zoo) {
this.zoo = zoo;
};
Animals.prototype.feed = function() {
//feed the animal
};
Animals.prototype.Monkey = function() {
this.Animals = Animals.prototype;
};
Animals.prototype.Monkey.prototype = Monkey.prototype;
Animals.prototype.Crocodile = function() {
this.Animals = Animals.prototype;
};
Animals.prototype.Crocodile.prototype = Crocodile.prototype;
With the intention that I should be able to do the following:
var animals = new Animals("NY");
var monkey = new animals.Monkey();
monkey.feed();
I'm receiving an error that says that monkey.feed() is not a function. I'm assuming i'm doing something wrong with the way i'm inheriting the Monkey function inside the Animal constructor function but for the life of me I haven't been able to find the solution.
What is the correct method I should be using to namespace these functions?
I have seen quite some stuff, but abusing prototypes for namespaces, what the heck. What's wrong with a nice and simple:
var Animals = {
Crocodile: {
}
}
Or if you want the constructor way:
var Animals = function () {
return {
Crocodile: function () {}
}
};
var a = new Animals();
var c = new a.Crocodile();

Categories