首页 > 解决方案 > Javascript类覆盖方法

问题描述

我有这门课

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"
    }
}

// 一些接口(方法)

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

我的问题是为什么不覆盖构造函数attack(target)takeDamage(damage)继承的方法。我知道这可能以前被问过,但我找不到它对不起。

标签: javascriptecmascript-6

解决方案


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

例如,考虑一个类 Fruit

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

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

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

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

让我们在 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 }

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

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

> Object.getPrototypeOf(banana);
Fruit {}

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

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

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

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

那香蕉怎么了?

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

因为我们叫f上banana,banana.cutIntoPieces现在已经不相关了Fruit.prototype.cutIntoPieces。虽然 orange 和 papaya 继承了原型的这个方法,banana 有自己的:

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

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

但是如何让香蕉在切成块时与其他水果的行为相同呢?让我们删除它自己的cutIntoPieces

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

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

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

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

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


推荐阅读