0

This may be a non-problem, but I have a feeling learning about proper practices will help me learn proper javascript techniques! I'm just starting with using objects and attaching functions to them. Here is the current setup I am using for a little combat simulator:

var standardAttack = function(attacker,target){
    if(!target.alive) return false;
    var damage = r(attacker.damage)+1-target.def;
    if(damage>0) {
        target.hp -= damage;
        sendMessage(attacker.name+" hits "+target.name+" for "+damage.toString());
        if(target.hp<1){
            sendMessage(target.name+[" is torn asunder!"," has it's head ripped off!"," is trampled into the dirt!"," is sliced in half!"," is smashed to pieces!"," falls to the ground, gurgling!"," flails and dies on the ground!"].sample()+" by "+attacker.name);
            target.alive = false;
        }
    } else {
        sendMessage(attacker.name+" hits "+target.name+" but no damage is dealt");
    }   
}

While the Creature object looks like this:

class Creature {
    constructor() {
        this.name = getName();
        this.alive = true;
        this.hp = 6;
        this.def = 0;
        this.damage = 6;
        this.attack = standardAttack;
    }
}

There are various different types of attacks in a similar format to standardAttack, and when needed, creature.attack is changed.

From this, attacks are started with:

creature1.attack(creature1,creature2);

or something along those lines. Looking at this, I feel like there is a more efficient way to access creature1.name and creature1.damage than passing itself into it's own function, a needless duplication. In addition, I don't think I am wrong in saying that it just looks bad! I feel that a more suitable format would be:

creature1.attack(creature2);

But currently, the standardAttack function is unable to find the damage or name values of the creature1 object. My question is this - is there any way to access the details of creature1 without passing the whole object into it's own function?

Please let me know if there are any changes I need to make to the question as well.

Spwack
  • 33
  • 4
  • 2
    In the Creature class constructor, you can do something like: ```this.attack = enemy => standardAttack(this, enemy)```. Then you call it as: ```creature1.attack(creature2)``` – Dov Rine Jul 28 '19 at 14:31

1 Answers1

0

You have a few options. For a SO post on "this": How does the "this" keyword work?

Option 1, minimal code change:

You can bind your attack function to the current class so that your attack function has access to the "this" context. e.g.:

class Creature {
    constructor() {
        this.attack = standardAttack.bind(this)
    }
}

In your standardAttack definition:

function standardAttack(target) {
    console.log(this.attack)
}

Alternatively, using the same bind approach, whenever you call creature.attack you can make sure it's called with the correct context, e.g.: creature.attack.call(creature, target).

Option 2, better OOP pattern:

Define a method of the class "attack" so that "attack" semantically belongs to the Creature rather than some generic function. In JavaScript this is nothing more than semantics since you'll still need to make sure the method is called with the correct context -- or bind the method to the right context via the approach mentioned above.

class Creature {
    attack(target) {
        console.log(this.attack)
    }
}

If you're using babel with the right rule enabled (I think class fields? / property initializers) you can do this shorthand which will automatically bind the method to the class instance. Word of caution, it doesn't add it to the prototype chain.

class Creature {
    attack = target => {
        console.log(target)
    }
}

If "attack" is shared by multiple entities, you can define the method in some base class and inherit it:

class AggressiveEntity {
    attack() {}
}

class Creature extends AggressiveEntity {}
noahnu
  • 3,013
  • 2
  • 15
  • 37
  • The bind approach seems to be exactly what I am looking for, but currently, standardAttack.bind(this) seems to be binding the entire window, not the specific Creature object. Am I doing something wrong? – Spwack Jul 28 '19 at 16:10
  • 1
    @Spwack where are you calling "bind"? If you do it in the global context, then "this" will refer to the global context. I'd recommend reading the link I posted about the "this" keyword. – noahnu Jul 28 '19 at 18:17
  • this.attack = standardAttack.bind(this) is called in the Creature constructor, then this.name and this.damage is called in standardAttack – Spwack Jul 29 '19 at 14:47
  • @Spwack how are you instantiating the creature object? – noahnu Jul 29 '19 at 16:43