2

I have this class

class MyClass {
    constructor(name, health, damage){
      INamable(this)
      IHealth(this)
      IDamage(this)
      IAttack(this)
      ITakeDamage(this)

      this.setName(name)
      this.setHealth(health)
      this.setDamage(damage)
    }

    attack(target){
        target.takeDamage(this.getDamage());
    }

    takeDamage(damage){
        this.setHealth(this.getHealth() - damage);
        if(this.getHealth()<=0){
            this.die();
        }
    }

    toString(){
        return "myClassToString"
    }
}

// some of the interfaces (methods)

  function IAttack(object){
        let attack = function(){}
        object.attack = attack;
    }
    function ITakeDamage(object){
        let takeDamage = function(){}
        object.takeDamage = takeDamage;
    }

My question is why does attack(target) and takeDamage(damage) don't override the methods inherited in the constructor. I know this may be asked before but I can't find it sorry.

5
  • if you created an object with new MyClass(), they should be overwritten Commented Sep 8, 2018 at 19:00
  • @ChrisLi they ain't... I remove the IAttack and ITakeDamage from the constructor and the class logic was working properly, but then I call them in the constructor there is no logic at all Commented Sep 8, 2018 at 19:03
  • @undefined I think that this is concidered some sort of inheretance since the class gets some logic/fields outside of it's own implementation. Commented Sep 8, 2018 at 19:05
  • they worked when i ran, the only problem i had was the setters weren't declared Commented Sep 8, 2018 at 19:19
  • 1
    They don't get inherited prototypically, that's exactly the problem. The interface calls in the constructor created new methods as own properties of the constructed instance, which take precedence over the methods declared on the prototype object of the class. Commented Sep 8, 2018 at 19:25

1 Answer 1

5

Up in the comments, @Bergi made an assumption that you might be instantiating new MyClass objects in the run time and refer to them. Because you try to change a method of an instance of an MyClass object, and not its prototype, all new instances of MyClass (create with "new" keyword) will still inherit original MyClass's properties.

For example, consider a class Fruit

class Fruit {
  constructor() {
    this.pieces = 1;
  }

  cutIntoPieces(pieces) {
    this.pieces = pieces;
    return this;
  }
}

and a function f that takes any object and changes its property cutIntoPieces, setting it to a function that unconditionally returns null and does nothing else:

const f = object => {
  object.cutIntoPieces = () => null;
};

Let's play around with it a bit in Node REPL:

> banana = new Fruit();
Fruit { pieces: 1 }
> orange = new Fruit();
Fruit { pieces: 1 }
> papaya = new Fruit();
Fruit { pieces: 1 }
> f(banana);
undefined
> banana.cutIntoPieces(2);
null
> banana
Fruit { pieces: 1, cutIntoPieces: [Function] }
> orange.cutIntoPieces(3);
Fruit { pieces: 3 }
> papaya.cutIntoPieces(4);
Fruit { pieces: 4 }

You can see that calling f on banana changed its behavior when you want to cut it into pieces. This happened because now bananas has its own property cutIntoPieces, which is a function that unconditionally returns null and doesn't affect the object.

To redefine the method cutIntoPieces in all instances of the object, we need to change it in their prototype, which is Fruit:

> Object.getPrototypeOf(banana);
Fruit {}

To make such a function that takes a prototype of an object and changes a property of it, so that all instances of that object inherit the changed property, we need to remake our function f a bit. Let's declare another function and call it g:

const g = object => {
  object.cutIntoPieces = function (cuts) {
    this.pieces = 2 ** cuts;
    return this;
  };
};

Here, g redefined the method cutIntoPieces of any object to make cutting more efficient. Now, if we call g with Fruit.prototype, it will change the method cutIntoPieces of orange and papaya:

> g(Fruit.prototype);
undefined
> orange.cutIntoPieces(4);
Fruit { pieces: 16 }
> papaya.cutIntoPieces(10);
Fruit { pieces: 1024 }

What's up with banana then?

> banana.cutIntoPieces(2);
null
> banana
Fruit { pieces: 1, cutIntoPieces: [Function] }

Because we called f on banana, banana.cutIntoPieces is now not related to Fruit.prototype.cutIntoPieces. While orange and papaya have this method inherited from the prototype, banana has its own:

> orange.cutIntoPieces === Fruit.prototype.cutIntoPieces
true
> papaya.cutIntoPieces === Fruit.prototype.cutIntoPieces
true
> banana.cutIntoPieces === Fruit.prototype.cutIntoPieces
false

Which is fine, I guess. In case you only want to change behavior of one instance, you can define its own property, its own method; on the other hand, when you need to change behavior of all instances that have methods inherited from prototype, you can change their prototype.

But how to get banana behave identically to other fruit when cut into pieces? Let's delete its own cutIntoPieces!

> delete banana.cutIntoPieces
true
> banana
Fruit { pieces: 1 }
> banana.cutIntoPieces(2)
Fruit { pieces: 4 }

See, after you remove object's own property, another one, with the same name, is inherited from the prototype, when there is one:

> banana.cutIntoPieces === Fruit.prototype.cutIntoPieces
true

Now banana, orange and papaya behave identically.

Hope it helps and best of luck!

Sign up to request clarification or add additional context in comments.

2 Comments

Better use f(Bananas.prototype) than g(new Bananas). Also don't use __proto__ at all, it's deprecated in favour of Object.getPrototypeOf :-)
Appreciate the note, and you're absolutely right. The answer is no longer proposing function g as the right way of doing things :) And I also replaced •.__proto__ with Object.getPrototypeOf(•).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.