Javascript 类重写方法

Al *_*ram 2 javascript ecmascript-6

我有这门课

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"
    }
}
Run Code Online (Sandbox Code Playgroud)

// 一些接口(方法)

  function IAttack(object){
        let attack = function(){}
        object.attack = attack;
    }
    function ITakeDamage(object){
        let takeDamage = function(){}
        object.takeDamage = takeDamage;
    }
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么attack(target)takeDamage(damage)重写构造函数中继承的方法。我知道以前可能有人问过这个问题,但抱歉我找不到。

ris*_*hat 5

在评论中,@Bergi 假设您可能在运行时实例化新的 MyClass 对象并引用它们。因为您尝试更改 MyClass 对象实例的方法,而不是其原型,所以所有新的 MyClass 实例(使用“new”关键字创建)仍将继承原始 MyClass 的属性。

例如,考虑一类 Fruit

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

  cutIntoPieces(pieces) {
    this.pieces = pieces;
    return this;
  }
}
Run Code Online (Sandbox Code Playgroud)

f以及一个接受任何对象并更改其 property 的函数cutIntoPieces,将其设置为无条件返回 null 且不执行任何其他操作的函数:

const f = object => {
  object.cutIntoPieces = () => null;
};
Run Code Online (Sandbox Code Playgroud)

让我们在 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 }
Run Code Online (Sandbox Code Playgroud)

您可以看到,f当您想将香蕉切成碎片时,调用香蕉会改变它的行为。发生这种情况是因为现在bananas有了自己的property cutIntoPieces,它是一个无条件返回null并且不影响对象的函数。

要在对象的所有cutIntoPieces实例中重新定义方法,我们需要在它们的原型(即 Fruit)中更改它:

> Object.getPrototypeOf(banana);
Fruit {}
Run Code Online (Sandbox Code Playgroud)

为了创建一个接受对象原型并更改其属性的函数,以便该对象的所有实例都继承更改后的属性,我们需要f稍微重新创建我们的函数。让我们声明另一个函数并调用它g

const g = object => {
  object.cutIntoPieces = function (cuts) {
    this.pieces = 2 ** cuts;
    return this;
  };
};
Run Code Online (Sandbox Code Playgroud)

这里,g重新定义了任意对象的方法cutIntoPieces,让切割更加高效。现在,如果我们用Fruit.prototype调用,它将改变orange和papaya的g方法:cutIntoPieces

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

那香蕉怎么了?

> banana.cutIntoPieces(2);
null
> banana
Fruit { pieces: 1, cutIntoPieces: [Function] }
Run Code Online (Sandbox Code Playgroud)

因为我们叫f香蕉,banana.cutIntoPieces现在已经不相关了Fruit.prototype.cutIntoPieces。虽然橙子和木瓜有从原型继承的这个方法,但香蕉有自己的:

> orange.cutIntoPieces === Fruit.prototype.cutIntoPieces
true
> papaya.cutIntoPieces === Fruit.prototype.cutIntoPieces
true
> banana.cutIntoPieces === Fruit.prototype.cutIntoPieces
false
Run Code Online (Sandbox Code Playgroud)

我想这很好。如果您只想更改一个实例的行为,您可以定义它自己的属性、它自己的方法;另一方面,当您需要更改所有具有从原型继承的方法的实例的行为时,您可以更改它们的原型。

但如何让香蕉在切成块后表现得与其他水果相同呢?让我们删除它自己的cutIntoPieces

> delete banana.cutIntoPieces
true
> banana
Fruit { pieces: 1 }
> banana.cutIntoPieces(2)
Fruit { pieces: 4 }
Run Code Online (Sandbox Code Playgroud)

看,当你删除对象自己的属性后,另一个同名的属性将从原型继承,当有一个时:

> banana.cutIntoPieces === Fruit.prototype.cutIntoPieces
true
Run Code Online (Sandbox Code Playgroud)

现在香蕉、橙子和木瓜的行为是相同的。

希望它有帮助,祝你好运!