I completely understand the concept of prototype inheritance in JavaScript if the methods in the constructor are the same for all instances. But what if I want to create objects via constructors but want the methods to produce a different result for each instance?
function Player (health, power, stamina) {
this.health = health;
this.power = power;
this.stamina = stamina;
this.recovery = function () {
return this.health += 20;
}
}
var hulk = new Person ( 100,80,60 );
var superman = new Person ( 100,70,50);
My problem here is I want hulks recovery to add 20 when I call the recovery method but I want superman’s to add 40. If I add the method to the prototype instead of directly into the object I still want that method to be only for the hulk and a different one for superman. Do I create object literals for this and specify the different methods for each object? This seems long wielded if I had hundreds of characters! Or do I add another multiple methods to the Person prototype even if it’s only going to be used by one instance? What’s the best way to make superman’s recovery better than the hulks whilst still using a constructor ? Thanks all.
Pass a 4th parameter to Player, one which indicates health regen:
function Player(health, power, stamina, healthRegen) {
this.health = health;
this.power = power;
this.stamina = stamina;
this.healthRegen = healthRegen;
this.recovery = function() {
return this.health += this.healthRegen;
}
}
var hulk = new Person(100, 80, 60, 20);
var superman = new Person(100, 70, 50, 40);
If most characters had a particular amount of health regen (say, 20), and you only wanted superman and a couple others to have different health regen, you could use a default parameter to avoid having to write 20 so much:
function Player(health, power, stamina, healthRegen = 20) {
this.health = health;
this.power = power;
this.stamina = stamina;
this.healthRegen = healthRegen;
this.recovery = function() {
return this.health += this.healthRegen;
}
}
var hulk = new Person(100, 80, 60);
var superman = new Person(100, 70, 50, 40);
var normalPerson = new Person(50, 50 50);
Above, see how you only need to pass the 4th parameter when the desired health regen is other than 20; otherwise, it'll default to 20.
Related
I am working on an assignment for a JS course.
Instructions say to:
setHrs() function takes input of hours worked. function should save input to its instance variable name hrs. no returns.
Okay, so I am pretty much stuck. Below is my code, where I define the employees, give them properties, share those properties between Employee --> Employee1 --> Employee2
Once I got Employees sorted out, I need to create functions within Hourly() that I can then call on later in the program; getRate() and getHrs().
I need pointers for assigning new values to this.rate and this.hrs, here is my code:
//Prince of Wales, Bed and Breakfast Resort
function Employee(id, name, hiredate, position){
this.id = id;
this. name = name;
this.hiredate = hiredate;
this.position = position;
}
Employee.prototype.hired = "Employed";
var Employee1 = new Employee("4", "Blackadder",
"06-03-1902", "butler");
var Employee2 = new Employee("5", "Baldrick",
Employee1.hiredate, "who knows");
// printed out to test prototype works... everything above can be printed
function Hourly(rate, hrs) {
this.rate = 0;
this.hrs = 0;
this.setHrs = function(){
// set Employee1 hours
// set Employee2 hours
}
this.setRate = function() {
// set Employee1 rate
// set Employee2 rate
}
}
Hourly.prototype = new Employee();
Employee1.prototype.hrs = 50;
Employee2.prototype.hrs = 25;
Employee1.prototype.rate = 4;
Employee2.prototype.rate = 1;
console.log(
"Name : " , Employee1.name ,
" Hourly Rate : ", Employee1.setRate(),
" Hours Worked : ", Employee1.setHrs(),
);
console.log(
"Name : " , Employee2.name ,
" Hourly Rate : ", Employee2.setRate(),
" Hours Worked : ", Employee2.setHrs(),
);
I had thought of something else that might work, it doesn't, but I might be on the right track???
this.setHrs = function(){
Employee1.hrs = 50;
Employee2.hrs = 25;
}
this.setRate = function() {
Employee1.rate = 4;
Employee2.rate = 1;
}
I have also thought, maybe rate and hrs should be arrays and I can push values to them, but everything I tried didn't add values to the arrays. Plus, if I do that then when I print the array, I could end up with more problems.
SOLUTION EDIT:
function Employee(id, name){
this.id = id;
this. name = name;
} // end of employee()
function Hourly(id, name) {
Employee.call(this,id,name);
var rate = 0.0;
var hrs = 0.0;
this.setHrs = function(time){ this.hrs = time; }
this.setRate = function(pay) { this.rate = pay; }
My main issue may have been the variable set up. I thought I understood prototypal inheritance, but there were some small details that caused my code not to run. once I changed my code from
this.rate = rate;
this.hrs = hrs;
to
var rate = 0;
var hrs = 0;
it was pretty much smooth sailing from there. Additionally, I needed to call the previous function Employee(). My employee variables are defined later in the code, but they are pretty much set up the same except for one important change... calling the appropriate function.
var Employee1 = new Hourly("2262124", "Blackadder");
Employee1.setHrs(50);
Employee1.setRate(4);
Employee1.getPayCheck();
console.log(" ");
previously, I called Employee() and that worked for assigning values to name and id, but not for setting rate and hrs which are "further" down in the inheritance chain. I then passed ALL properties to the Hourly() function and my properties were getting their values appropriately.
I just wanted to post my solution here for others who may be having issues practicing with inheritance. Thank you for reading and commenting!!
The setter functions need to take a parameter, and then they should update the property of the current object.
this.setHrs = function(hours){
this.hrs = hours;
}
You shouldn't be accessing Employee1 and Employee2 inside the methods, because the caller can have other variables containing these objects. They use those variables when they call the method:
Employee1.setHrs(50);
Employee2.setHrs(25);
Part of the problem was also the , instead of + in console.log
function Employee(id, name, hiredate, position){
this.id = id;
this. name = name;
this.hiredate = hiredate;
this.position = position;
this.rate = 0;
this.hours = 0;
}
Employee.prototype.hired = "Employed";
Employee.prototype.setHrs =function(hours){
this.hours = hours;
}
Employee.prototype.setRates = function(rates){
this.rate = rates;
}
function Hourly (employeeContext,hours, rates){
employeeContext.setRates(rates);
employeeContext.setHrs(hours);
}
var Employee1 = new Employee("4", "Blackadder",
"06-03-1902", "butler");
var Employee2 = new Employee("5", "Baldrick",
Employee1.hiredate, "who knows");
//calling Hourly
Hourly(Employee1, 4, 25);
Hourly(Employee2, 1, 50);
console.log(
"Name : " + Employee1.name +
" Hourly Rate : " + Employee1.rate+
" Hours Worked : "+ Employee1.hours
);
console.log(
"Name : " + Employee2.name +
" Hourly Rate : "+ Employee2.rate+
" Hours Worked : "+ Employee2.hours
);
setHrs() function takes input of hours worked
You should be passing some parameters to this function. A setter function will take some input and "set" some object variable.
setHrs(hours)
You should do this for each Employee object you create.
Employee1.setHrs(50)
Employee2.setHrs(25)
I hope this helped.
EDIT
For clarification on what I meant:
If you were to create an Employee object like you already have
function Employee(id, name, hiredate, position){
this.id = id;
this.name = name;
this.hiredate = hiredate;
this.position = position;
}
Then you can instantiate Employees like this:
var johnSmith = new Employee("6", "John Smith", "04-28-2017", "Chef")
Now if we were to modify the Employee class like this:
function Employee(id, name, hiredate, position){
this.id = id;
this.name = name;
this.hiredate = hiredate;
this.position = position;
this.setHrs = function (hours) = {
this.hours = hours;
}
this.setRate = function (rate) = {
this.rate = rate;
}
}
Now you are able to instantiate the Employee object like I did above but now you can call these member functions on the object.
johnSmith.setHours(50);
johnSmith.setRate(25);
There are ways to handle inheritance if you had to handle both salary and hourly workers, but this solution will work if you only have to deal with hourly workers.
Here's part of my code:
class Light {
constructor(xpos,zpos,ypos,range,diffuser,diffuseg,digguseb,intensity,angle,exponent) {
this.xpos = xpos;
this.ypos = ypos;
this.zpos = zpos;
this.range = range;
this.diffuser = diffuser;
this.diffuseg = diffuseg;
this.diffuseb = diffuseb;
this.intensity = intensity;
this.angle = angle;
this.exponent;
[...]
Is there any way to move all given argument variables to this so I can access them later?
var lt = new Light(0,12,15,...);
alert(lt.zpos); //outputs '12'
I'm looking for a solution to put those 11 this lines to one
This does what you desire. The portion in mapArgsToThis which gets the argument names was taken from here. mapArgsToThis would be a helper function you would use when you want to be lazy.
var mapArgsToThis = function(func, args, thisPointer) {
var argsStr = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];
var argNames = argsStr.split(',').map(function(arg) {
return arg.replace(/\/\*.*\*\//, '').trim();
}).filter(function(arg) {
return arg;
});
var argValues = Array.prototype.slice.call(args);
argNames.forEach(function(argName, index) {
thisPointer[argName] = argValues[index];
});
};
var MyConstructor = function(xpos,zpos,ypos,range,diffuser,diffuseg,digguseb,intensity,angle,exponent) {
mapArgsToThis(MyConstructor, arguments, this);
};
var myInstance = new MyConstructor(1,2,3,4,5,6,7,8,9,0);
console.log(myInstance);
Even though this is a solution, I don't recommend it. Typing out the argument mapping to your this properties is good for your fingers and is easier for others to read and know what's going on. It also doesn't allow for any processing of the argument values prior to assignment onto this.
I made this two classes below in the code, and I am not sure if I made it in a right oop way. Is it good that I made geometry class and vertex like two distinct classes or maybe they can be one father and child? Another problem is when I call geometry show method and it returns me undefined.
//////////////////////////////////////////
// VERTICES
//////////////////////////////////////////
function Vertex(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Vertex.prototype.show = function () {
return this.x + ":" + this.y + ":" + this.z;
}
//////////////////////////////////////////
// GEOMETRY
//////////////////////////////////////////
function Geometry() {
this.vertices = [];
}
Geometry.prototype.push = function(v) {
this.vertices.push(v);
}
Geometry.prototype.show = function() {
for(var i = 0; i < this.getVertexCount(); i++){
this.vertices[i].show();// undefined!
}
}
Geometry.prototype.getVertexCount = function() {
return this.vertices.length;
}
/////TEST/////
function test() {
v = new Vertex(2,4,6);
console.log(v.show());
g = new Geometry();
g.push(v);
console.log(g.show()); //undefined
}
I am not sure if I made it in a right oop way.
Seems fine, I can't see any common mistakes.
My doubt is for geometry class that has a vertice object field inside. Is it correct or the are better way to do it?
Depends on what you need. There's nothing inherently wrong with it, but if you told us your use case we might find a different solution.
Is it good that I made geometry class and vertex like two distinct classes or maybe they can be one father and child?
No, there should not be any inheritance. There is no is-a relationship between them. They should be distinct classes, one using the other.
Another problem is when I call geometry show method and it returns me undefined.
Yes, because it doesn't return anything. All those strings that it gets from the invocation of the Vertice show() calls are thrown away. It seems like you want something like
Geometry.prototype.show = function() {
var result = "";
for (var i = 0; i < this.getVertexCount(); i++) {
if (i > 0)
result += "\n";
result += this.vertices[i].show();
}
return result; // not undefined!
}
I am trying to learn how to work with javascripts prototype, I am only getting into it now. Please Excuse me if I ask ridiculously stupid questions
I just have a few pre-questions:
Is it worth learning? I mean it looks like a structured/clean
approach to me?
Do/should you use this with jQuery this?
is there any major problems or reason not to use it and why isn't it commonly used or am i just slow?
Actual Question:
I have the following code:
var BudgetSection = function BudgetSection(name ) {
this.id = "";
this.name = name;
this.monthlyTotal = 0.00;
this.yearlyTotal = 0.00;
this.subTotal = 0.00;
this.lineItems = [];
};
BudgetSection.prototype.calculateSubTotal = function() {
this.subTotal = ((12 * this.monthlyTotal) + this.yearlyTotal);
};
function BudgetLineItem(name) {
this.id = "";
this.name = name;
this.monthlyAmount = 0.00;
this.yearlyAmount = 0.00;
}
BudgetLineItem.prototype = {
totalAmount : function() {
var result = ((12 * this.monthlyAmount) + this.yearlyAmount);
return result;
}
};
var budgetSections = [];
section = new BudgetSection("test1");
section.lineItems.push(new BudgetLineItem('sub'));
section.lineItems.push(new BudgetLineItem('sub2'));
section.lineItems.push(new BudgetLineItem('sub3'));
budgetSections.push(section);
section = new BudgetSection("test2");
section.lineItems.push(new BudgetLineItem('sub'));
section.lineItems.push(new BudgetLineItem('sub2'));
section.lineItems.push(new BudgetLineItem('sub3'));
budgetSections.push(section);
section = new BudgetSection("test3");
section.lineItems.push(new BudgetLineItem('sub'));
section.lineItems.push(new BudgetLineItem('sub2'));
section.lineItems.push(new BudgetLineItem('sub3'));
budgetSections.push(section);
// first iterate through budgetSections
for ( var t = 0; t < budgetSections.length; t++) {
var sec = budgetSections[t];
console.log(sec);
// iterate through each section's lineItems
for (var q = 0; q< budgetSections[t].lineItems.length ; q++) {
var li = budgetSections[t].lineItems[q];
console.log(li);
}
}
the first BudgetSection "test1" is at index 0 in the budgetSections array. how can i assign the id to "section_".
And then also how can i set the id of BudgetLineItem like so: lineItemRow_<section_index><lineitem_index>
Also finally n the for loop what would be the best way to generate html?
I personally never use the new keyword if I can avoid it and do pure prototype-based programming with Object.create. Here's a simple example. I create a prototype-object called rectangle and then create an object called myRectangle which inherits from rectangle.
var rectangle = {
init: function( x, y, width, height ) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
},
move: function( x, y ) {
this.x += x;
this.y += y;
}
};
var myRectangle = Object.create( rectangle );
myRectangle.init( 0, 0, 2, 4 );
myRectangle.move( 3, 5 );
To explain in more depth what happens here, Object.create makes a new object with a specified prototype. When we access a property on an object (like init or move), it first checks the object itself. If it can't find it there, it moves up to the object's prototype and checks there. If it's not there, it checks the prototype's prototype, and keeps going up the prototype chain until it finds it.
When we call a function on an object (myRectangle.init()), this inside the function refers to that object, even if the function definition is actually on the prototype. This is called delegation - an object can delegate its responsibilities to its prototype.
A more class-like way to do this is:
function Rectangle( x, y, width, height ) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
Rectangle.prototype.move = function( x, y ) {
this.x +=x;
this.y +=y;
};
var myRectangle = new Rectangle( 0, 0, 2, 4 );
myRectangle.move( 3, 5 );
The problem is when we need to do a deeper inheritance hierarchy:
function Parent() {
/* expensive and possibly side-effect inducing initialization */
}
Parent.prototype.parentMethod = function() {};
function Child() {}
Child.prototype = new Parent();
We have to initialize a Parent object when all we really want is to set the Child prototype to an object based on Parent.prototype. Another option is:
Child.prototype = Object.create( Parent.prototype );
But now we've got this confusing, convoluted mess of prototype-based and class-based code. Personally, I like this instead:
var parent = {
parentMethod: function() {}
};
// Using underscore for stylistic reasons
var child = _.extend( Object.create( parent ), {
childMethod: function() {}
});
var instance = Object.create( child );
instance.parentMethod();
instance.childMethod();
No new keyword needed. No fake class system. "Objects inherit from objects. What could be more object-oriented than that?"
So what's the catch? Object.create is slow. If you're creating lots of objects, it's better to use new. You can still use Object.create to set up the prototype chain, but we'll have to wait a bit for browsers to optimize it enough for lots of instantiation.
Have you tried budgetSections[0].id = 'yourID';?
I'm inheriting an object from the EASELJS library.
To simplify the problem, I'm reducing the code into the minimal form.
I have a class:
this.TESTProg = this.TESTProg || {};
(function() {
var _jsbutton = function(x, y, text, icon) {
p.init(x, y, text, icon);
};
var p = _jsbutton.prototype = new createjs.Container();
p.x = 0;
p.y = 0;
p.text = null;
p.icon = null;
p.init = function(x, y, text, icon) {
this.x = 0 + x;
this.y = 0 + y;
this.text = "" + text;
this.icon = null;
};
TESTProg._jsbutton = _jsbutton;
})();
Then I use it in another js object:
var buttoncancel = new SJSGame._jsbutton(
profileselConfig.cancelx, //this is defined in another jsfile:
profileselConfig.cancely,
"cancel", "_cancel.png");
console.log( buttoncancel.y ); //this gives 240
var buttoncancel2 = new SJSGame._jsbutton(
profileselConfig.cancelx,
profileselConfig.cancely - 40,
"cancel", "_cancel.png");
console.log( buttoncancel.y ); //this gives 200
console.log( buttoncancel2.y ); //this gives 200
buttoncancel2.y = 100;
console.log( buttoncancel.y ); //this now gives 200 (not changed by the second object)
console.log( buttoncancel2.y ); //this now gives 100
The config file:
var _profileselConfig = function(){
this.cancelx = 0;
this.cancely = 240;
};
profileselConfig = new _profileselConfig();
And what am i doing wrong?
I'm already using 0 + to avoid passing the reference and it's not working. What should I do now? Any suggestions? Thanks.
You should probably be calling this.init rather than p.init in your constructor.
When you call p.init, the this inside of init refers to the prototype. Thus, whenever you create an instance, your p.init call modifies the prototype for all _jsbutton objects.
That's why both buttons have the same x/y values: they both get their position from the same prototype, and the last-run constructor set the prototype values. When you set buttoncancel2.y outside of the constructor, you gave that instance its own y property, so it no longer used the shared prototype value.
If you call this.init in your constructor, then the this in init will refer to your newly-created instance. The instances will no longer use the shared prototype values for x, y, text, and icon.
Side note: "I'm already using 0 + to avoid passing the reference" -- this is not necessary, because primitive types are always copied.