0

I am trying to follow Kyle Simpson's idea of actual prototypal inheritance using only objects instead of functions. I wanted to see if I could use Object.create and Object.assign to add mixins to an object, which totally works. I then tried to take it a step further and base some of the values of the properties in a mixin on the value of a property in the base object. I always get NaN for the value I'm trying to work with. Here's the code:

function generatorFactory() {
    var basePlayer = {
        x: 0,
        y: 0,
        baseHealth: 50
    };

    var generatePlayer = function() {
        return basePlayer;
    };
    return generatePlayer;
}

var playerGenerator = generatorFactory();
var basicPlayerObject = playerGenerator();

var heavyArmor = {
    defense: 15,
    attack: 9
};

var lightArmor = {
    defense: 7
};

var swordsman = {
    health: this.baseHealth + 10
};

var knifeFighter = {
    health: this.baseHealth - 10
};

var sigurd = Object.assign(Object.create(basicPlayerObject), heavyArmor, swordsman);

console.log(sigurd.health);
console.log(sigurd.defense);
console.log(sigurd.attack);
console.log("Player coordinates: (" + sigurd.x + ", " + sigurd.y +")");

I want to have the swordsman mixin adjust the player's base health. I tried using 'this', but it did not help. I'm starting to think I'm approaching this completely wrong. What exactly am I doing wrong here?

redeagle47
  • 1,355
  • 4
  • 14
  • 26
  • "*using only objects instead of functions*" - that doesn't make much sense. You're still using functions as methods, and by not using functions to create your objects you're loosing a lot of flexibility. – Bergi May 13 '16 at 22:17
  • Could you unpack that a bit? I'm still learning this other way of working with prototypal inheritance, so I may not be understanding or representing it correctly. – redeagle47 May 17 '16 at 11:30

2 Answers2

1

The problem is the way you're using this. When you're defining an object, this still refers to the surrounding scope, not the new object you're defining:

var self = this;
var swordsman = {
    checkThis: self === this // true
};

One way of accomplishing what you're trying to do is to change those objects into functions.

function initSwordsman(obj) {
    obj.health = obj.baseHealth + 10;
}
var sigurd = Object.assign(Object.create(basicPlayerObject), heavyArmor);
initSwordsman(sigurd);
Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
  • 1
    Okay, I see what you mean. 'this' referred to the global scope. I was hoping it wouldn't be evaluated until after the mixin was assigned to the base object, or maybe re-evaluated. I see why that doesn't work, though. Thanks, I'll accept the answer as soon as the timer on it runs out. – redeagle47 May 13 '16 at 21:31
1

The this in your object literals doesn't refer to what you expect. If you want it dynamically refer to the object the property is accessed on, you will need to use a getter:

var swordsman = {
    health: {
        get() { return this.baseHealth + 10; },
        enumerable: true,
        configurable: true
    }
};

var knifeFighter = {
    health: {
        get() { this.baseHealth - 10 },
        enumerable: true,
        configurable: true
    }
};

and you need to pass those property descriptors to Object.create instead of Object.assign:

var sigurd = Object.assign(Object.create(basicPlayerObject, swordsman), heavyArmor);
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375