2

I wrote this snippet of code, trying to create an impromptu fighting game. It uses the old style function keyword, therefore when I want to use a helper function isAlive I'm forced to do a very ugly 'this' binding.

I'm trying to grasp how 'this' behaves differently with arrow functions but when I change the first line to ES6 syntax and drop the binding it always returns false.

let isAlive = () => this.health > 0;

What am I doing wrong? I'm pretty sure the syntax itself is OK.

let isAlive = function () { return this.health > 0};

let getFighter = function (color, strength) {
    return {
        color: color,
        health: 100,
        strength: typeof strength !== 'undefined' ? strength : 1,
        attack: function (what) {
            hit_points = Math.floor(Math.random() * 10 * this.strength);
            document.writeln(this.color + ' fighter attacks ' + what.color + ' trying to deal ' + hit_points + ' damage.');
            what.get_hurt(hit_points);
        },
        get_hurt: function (hit_points) {
            if ( !isAlive.bind(this)() ) {
                document.writeln(this.color + ' fighter already dead!');
                document.writeln();
                return;
            } 
            this.health = this.health - hit_points;
            document.writeln(this.color + ' received ' + hit_points + ' damage and has ' + this.health + ' HP left.');
            if ( !isAlive.bind(this)() ) {
                document.writeln(this.color + ' fighter died!');
            }
            document.writeln();
        }
    };
};

blue = getFighter('Blue', 3);
red = getFighter('Red');


console.log(red);
console.log(blue);

while (isAlive.bind(blue)()) {
    red.attack(blue);
}

red.attack(blue)
iknownothing
  • 125
  • 9
  • Should you `isAlive` function not be with the normal `function` syntax. Should work if you do that I think. The `this` inside is called `lexical this`. Have a look at https://stackoverflow.com/questions/34696686/what-is-lexical-this for an explanation. – putvande Oct 03 '17 at 17:05
  • 2
    Why is `isAlive` outside of the object that you're checking? If `this.health` is undefined, by checking with `typeof(this.health)`, then that statement will always be false. It sounds like you want to put it in the same object as `attack`, `get_hurt`, etc...? –  Oct 03 '17 at 17:05
  • There is only a fighter object currently but I want to add different objects, and apply isAlive() to them as well. This is just educational anyway, my main goal is to actually experiment with the language. – iknownothing Oct 03 '17 at 17:07

3 Answers3

2

An arrow function does not create its own this, the this value of the enclosing execution context is used.

By defining isAlive in a different execution context, you also bind the keyword to a different this.

If you want to take advantage of arrow functions, than declare them inside your function

function foo(){
  const isAlive = () => this.health > 100;
}

In your case, if you want a helper you either declare it as a part of the object, or uses ES6 Classes and class properties will do that for you.

Rosmarine Popcorn
  • 10,761
  • 11
  • 59
  • 89
1

Looking at your code example, I think we first need to understand how this works. this is will refer to your object/function that has been instantiated.

const getFighter = function(color, strength) {
  this.color = color;
  this.strength = strength && 1;

  return {
    health: 100,
    isAlive: this.health > 0,
    hit_points: () => Math.floor(Math.random() * 10 * this.strength),
    attack: (what) => {
      document.writeln(`${this.color} fighter attacks ${what.color} trying to deal ${this.hit_points} damage.`);
      what.get_hurt(this.hit_points);
      return null;
    },
    get_hurt: () => {
      if (!this.isAlive) {
        document.writeln(`${this.color} fighter already dead!`);
        return;
      }
      this.health = this.health - this.hit_points;
      document.writeln(`${this.color} received ${this.hit_points} damage and has ${this.health} HP left.`);
      if (!this.isAlive) {
        document.writeln(`${this.color} fighter died!`);
      }
    },
  };
};


const BlueFighter = new getFighter("Blue", 3);
const RedFighter = new getFighter("Red");

console.log('is Alive', BlueFighter.isAlive);
console.log('Health:', RedFighter.health);

You can see I take advantage of () => so that I can have access to this within the function. You wouldn't get this with a regular function unless you .bind(this)!

Tony Tai Nguyen
  • 1,502
  • 13
  • 27
0

You can define a parameter at isAlive function and pass the object or this to the function

let isAlive = ({health}) => health > 0; 

let getFighter = function(color, strength) {
  return {
    color: color,
    health: 100,
    strength: typeof strength !== 'undefined' ? strength : 1,
    attack: function(what) {
      hit_points = Math.floor(Math.random() * 10 * this.strength);
      document.writeln(this.color + ' fighter attacks ' + what.color 
        + ' trying to deal ' + hit_points + ' damage.');
      what.get_hurt(hit_points);
    },
    get_hurt: function(hit_points) {
      if (!isAlive(this)) {
        document.writeln(this.color + ' fighter already dead!');
        document.writeln();
        return;
      }
      this.health = this.health - hit_points;
      document.writeln(this.color + ' received ' + hit_points 
        + ' damage and has ' + this.health + ' HP left.');
      if (!isAlive(this)) {
        document.writeln(this.color + ' fighter died!');
      }
      document.writeln();
    }
  };
};

blue = getFighter('Blue', 3);
red = getFighter('Red');

console.log(red);
console.log(blue);

while (isAlive(blue)) {
  red.attack(blue);
}

red.attack(blue);

console.log(red);
console.log(blue);
guest271314
  • 1
  • 15
  • 104
  • 177