JavaScript类覆盖方法

时间:2018-09-08 18:54:05

标签: javascript ecmascript-6

我有这堂课

driver.execute_script("window.open('%s');" % url)

//一些接口(方法)

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)不覆盖构造函数中继承的方法。我知道这可能是以前问过的,但我找不到抱歉。

1 个答案:

答案 0 :(得分:4)

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

例如,考虑一个水果类

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,以提高切割效率。现在,如果我们使用Fruit.prototype调用g,它将更改orange和木瓜的方法cutIntoPieces

> 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.cutIntoPieces现在与Fruit.prototype.cutIntoPieces无关。尽管Orange和木瓜具有从原型继承的此方法,但香蕉有其自己的:

> 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

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

希望它能帮助您,并祝您好运!