Reputation: 329
Why am I getting NaN
instead of number when I click the button #plr1button1
?
Like I don't understand it I am looking at it for like 2 hours and I still can't figure out the mistake.
class Fighter {
constructor(atk, def, hp) {
this.atk = atk;
this.def = def;
this.hp = hp;
}
}
var Fighter1 = new Fighter(40, 5, 100);
var Fighter2 = new Fighter(30, 20, 100);
Fighter1.attack = function() {
var attack1 = this.atk + (Math.floor(Math.random() * 5) - 5) - Fighter2.def;
Fighter2.hp = Fighter2.hp - attack1;
document.getElementById("hp2").innerHTML = Fighter2.hp;
}
Fighter2.attack = function() {
var attack1 = this.atk + (Math.floor(Math.random() * 5) - 5) - Fighter1.def;
Fighter1.hp = Fighter1.hp - attack1;
document.getElementById("hp1").innerHTML = Fighter1.hp;
}
function random() {
var randomNum = Math.floor(Math.random() * 6) + 1;
/*var randomNum2 = Math.floor(Math.random() * 6) + 1;
var randomNum3 = Math.floor(Math.random() * 6) + 1;*/
if (randomNum === 1) {
document.getElementById("plr1button1").innerHTML = "Attack";
$("#plr1button1").bind("click", Fighter1.attack);
document.getElementById("plr2button1").innerHTML = "Attack";
}
}
Upvotes: 0
Views: 90
Reputation: 106375
You lose the context with that event handler; use .bind
to fix it.
$("#plr1button1").bind("click", Fighter1.attack.bind(Fighter1));
It's easy to detect it: just put the breakpoint into attack
method and check what's this
equal to when the method is called by as click
event handler.
There are several other issues with your code.
First, you can easily abstract a function that generates a random integer number in the given range - and reuse it instead of repeating the whole Math.floor(Math.random...
snippet.
Second, you mixed two concerns in your attack
code, changing both state (of attacked Fighter) and its representation. It's usually better to separate those.
Finally, you unnecessarily hardcoded all the actions in two Fighter instance - instead of using prototype
to store a single function.
For example, there's one way how this can be simplified:
class Fighter {
constructor(atk, def, hp) {
this.atk = atk;
this.def = def;
this.hp = hp;
}
attack(enemy) {
const hits = this.atk + _randomInRange(0, 5) - enemy.def;
enemy.hp -= hits;
enemy.render();
}
render() {
// updates the representation of Fighter
}
}
function _randomInRange(from, to) {
return from + Math.floor( Math.random() * (to + 1 - from) );
}
And here's a small (and incomplete) demo of that approach in action.
Upvotes: 3
Reputation: 26527
It's because how you are calling it, this
is not Fighter1
, it's the element that triggered the event (since you're using jQuery; vanilla JS it would be the event object) inside of Fighter1.attack
.
When you call a function directly like you are, it's going to use the function but won't maintain it's usual context. I.e., Fighter1.attack
in a callback is not the same as Fighter1.attack()
.
What you need to do is bind the functions to their respective this
so they maintain that inside.
Change your callback like this:
$("#plr1button1").bind("click", Fighter1.attack.bind(Fighter1));
Then, when it's triggered, this
will be Fighter1
as you intended.
You could also bind the function overall to not have to rebind:
Fighter1.attack = (function() {
var attack1 = this.atk + (Math.floor(Math.random() * 5) - 5) - Fighter2.def;
Fighter2.hp = Fighter2.hp - attack1;
document.getElementById("hp2").innerHTML = Fighter2.hp;
}).bind(Fighter1);
Repeat for Fighter2
as well.
Upvotes: 1