【问题标题】:Override method for just one instance in JavaScriptJavaScript 中仅一个实例的覆盖方法
【发布时间】:2016-12-19 14:02:54
【问题描述】:

在我的项目中,我使用 JavaScript 原型来实现继承。我有一个超级类型(如汽车)和许多子类型(奥迪/梅赛德斯/...)。每个汽车品牌都在使用“喇叭”,这是一个相当大的类型,不应该被复制。这种类型的一种方法应该被每个汽车品牌覆盖,但应该只保留在特定子类型的范围内。如果不为每个汽车子类型创建喇叭子类型,这是否可能? 谢谢!

http://codepen.io/trek7/pen/QGYaBW

function Horn() {
  this.level = '123dB';
}

Horn.prototype.doTutut = function() {
  return "Düdelüüüü!";
};

/********************************************/

function Car() {
  this.horn = new Horn();
}

Car.prototype.honk = function() {
  return this.horn.doTutut();
};

/********************************************/

Mercedes.prototype = new Car();

function Mercedes() {
  this.color = 'blue';
}

Mercedes.prototype.drive = function() {
  return "...BrumBrum...";
};

/********************************************/

Audi.prototype = new Car();

function Audi() {
  this.color = 'red';

  /* BAD Overwrite, but how can I achieve this functionality
   without creating another Horn subtype? */
  Horn.prototype.doTutut = function() {
    return "Tütütütütü!";
  };
}

Audi.prototype.honk = function() {
  return this.horn.doTutut();
};

/********************************************/

var car = new Car();      
car.honk();  // Düdelüüüü! - Right!

var mercedes = new Mercedes();      
mercedes.honk();  // Düdelüüüü! - Right!

var audi = new Audi();      
audi.honk();  // Tütütütütü! - Right!

mercedes.honk();  // Tütütütütü! - Wrong!

【问题讨论】:

    标签: javascript oop inheritance overriding prototype


    【解决方案1】:

    这里通常是参数化Horn 类,以便它接受要发出的声音:

    function Horn(sound) {
      this.sound = sound !== undefined ? sound : "Düdelüüüü!";
      this.level = '123dB';
    }
    
    Horn.prototype.doTutut = function() {
      return this.sound;
    };
    

    然后在Mercedes 内,您可以将声音传递给Car 超级构造函数,或者在this.horn 上设置它。


    如果你有一个完整的方法需要重写,你可以把它分配给this.horn.theMethod

    function Mercedes() {
      Car.call(this); // See below, important to do this first
      this.horn.theMethod = function() {
        // New implementation here
      };
    }
    

    ...但重要的是你先修复下面的继承问题。

    当然,如果您希望所有Mercedes 实例(例如)共享一个单个 Horn 实例,只需将具有适当方法的喇叭放在Mercedes.prototype 上。


    旁注:您的继承设置使用了经常重复的反模式。不要这样做:

    Mercedes.prototype = new Car();
    

    如果你这样做,有两个问题:

    1. Car 不能接受参数,并且

    2. Mercedes.prototype.constructor错了

    3. 因为Car 将一个对象 (new Horn) 分配给this,所以每个子类的所有实例(MercedesAudi 等)最终都会结束共享同一个对象(一个喇叭)。如果该对象是无状态的,那就太好了,但它应该在Car.prototype 上。如果它不是无状态的,则会在 Car 实例之间产生难以诊断的串扰。

    另外,Mercedes 永远不会调用 Car,它应该这样做是为了让 Car 有机会初始化它的实例部分。

    在 ES5 中,这是正确的做法:

    function Mercedes() {
      // Gives `Car` its chance to do init; could pass on args here if appropriate
      Car.call(this);
      this.color = 'blue';
    }
    
    // Set up the prototype chain    
    Mercedes.prototype = Object.create(Car.prototype);
    Mercedes.prototype.constructor = Mercedes;
    
    Mercedes.prototype.drive = function() {
      return "...BrumBrum...";
    };
    

    当然,在 ES2015 及更高版本(您现在可以使用转译)中,它只是:

    class Mercedes extends Car {
        drive() {
          return "...BrumBrum...";
        }
    }
    

    ...除非Mercedes 构造函数接受参数Car 不需要/使用。


    这是一个更新继承和跨所有子类实例共享喇叭的示例:

    // Horn with generic default
    function Horn(sound) {
        this.sound = sound !== undefined ? sound : "Düdelüüüü!";
        this.level = '123dB';
    }
    
    Horn.prototype.doTutut = function() {
      return this.sound;
    };
    
    // Car using a single generic horn across all Car instances
    function Car() {
    }
    Car.prototype.honk = function() {
        return this.horn.doTutut();
    };
    Car.prototype.horn = new Horn();
    
    // Mercedes with its own special horn
    function Mercedes() {
        Car.call(this);
    }
    Mercedes.prototype = Object.create(Car.prototype);
    Mercedes.prototype.constructor = Mercedes;
    Mercedes.prototype.horn = new Horn("Mercedes Honk!");
    
    // Audi with its own special horn
    function Audi() {
        Car.call(this);
    }
    Audi.prototype = Object.create(Car.prototype);
    Audi.prototype.constructor = Audi;
    Audi.prototype.horn = new Horn("Audi Honk!");
    
    // Usage
    var c = new Car();
    console.log(c.honk());
    var m = new Mercedes();
    console.log(m.honk());
    var a = new Audi();
    console.log(a.honk());

    【讨论】:

    • 是的,没错。但在我的(真实)案例中,它不仅仅是声音,它是一个完全不同的完整方法。
    • @trek7:您可以为this.horn.theMethod 分配一个新功能。我已经在答案中添加了一条注释。
    • @trek7:还添加了一个实例。
    • 感谢您的广泛回答。下次我肯定会使用ES2015! ;-) 最后我通过向 Car 添加一个额外的方法 hornHonk() 解决了我的问题,该方法由 honk() 调用。现在我能够在每个汽车子类型中继承 hornHonk 并保持 Horn 原样。有点奇怪,但这是唯一不重新设计我的整个项目的可能性。
    猜你喜欢
    • 2012-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    • 2010-09-28
    • 2012-05-09
    相关资源
    最近更新 更多