dave: Vererbung

Hi,

ich hab ein Problem bei der Vererbung in Javascript.

  
var chart = function(target){  
	this.ctx = target.getContext("2d");  
}  
  
var pieChart = function(target,config){  
	this.constructor(target);  
	//Verarbeite config etc.  
}  
  
pieChart.prototype = new chart();

In der letzten Zeile erfolgt die Vererbung.
Da ich hier an chart keinen Parameter übergebe, im Konstruktor aber eine Methode des Parameters aufrufe, wird hier eine Exception geworfen.

Gibt es einen Weg zu vererben ohne den Konstruktor der Elternklasse direkt aufzurufen?

Ansonsten wäre die einzige Lösung die mir einfällt im Konstruktor zu prüfen ob etwas übergeben wurde bevor ich darauf zugreife.

~dave

  1. Hallo,

    zum einen sagt Douglas Crockford in einem seiner Vorträge beim yahoo-theater oder wo das war, dass man "new" immer vermeiden kann und sollte, weil das nämlich irgendwie eine Inkonsitenz oder Fehlerquelle innerhalb der Sprache ist.

    Ansonsten kennt Javascript "arguments". Das ist eine Array der Argumente, die übergeben wurden.

    Wieso erzeugst Du aber eine Instanz einer Klasse, wenn diese einen Parameter benötigt, Du den aber nicht übergibst. So ganz gefühlt scheint mir das was unlogisch zu sein.

    Gruß

    jobo

    1. Hi,

      Wieso erzeugst Du aber eine Instanz einer Klasse, wenn diese einen Parameter benötigt, Du den aber nicht übergibst. So ganz gefühlt scheint mir das was unlogisch zu sein.

      Wie vererbst du?

      ~dave

      1. Hallo,

        Hi,

        Wieso erzeugst Du aber eine Instanz einer Klasse, wenn diese einen Parameter benötigt, Du den aber nicht übergibst. So ganz gefühlt scheint mir das was unlogisch zu sein.

        Wie vererbst du?

        nicht ich:

        http://www.crockford.com/javascript/javascript.html

        http://www.crockford.com/javascript/inheritance.html

        "Then it gets even better with the hoozit. With the hoozit I call new constructor, pass in the gizmo that says I want hoozit to inherit from gizmo, and I also pass it a constructor. I'll also pass it an object containing additional methods that I want it to add to its prototype."

        http://developer.yahoo.com/yui/theater/video.php?v=crockonjs-3

        Es gibt noch das hier:

        http://www.crockford.com/javascript/private.html - ist auch auf selfhtml-aktuell verlinkt.

        Und das:

        "You never need to use new Object() in JavaScript. Use the object literal {} instead."

        Gruß

        jobo

        1. Hi,

          http://www.crockford.com/javascript/inheritance.html

          In der inherits-Methode auf der Seite:
          p = (this.prototype = new parent());

          Genau das mache ich auch (nur über weniger Umwege).

          Wenn er in seiner function Parenizor(value) etwas in der Art value.foo() machen würde hätte er das gleiche Problem wie ich.

          Ich sehe übrigens auch keinen Grund dafür eine Function.prototype.method() zu machen. Ich finde das verwirrt eher.

          "Then it gets even better with the hoozit. With the hoozit I call new constructor, pass in the gizmo that says I want hoozit to inherit from gizmo, and I also pass it a constructor. I'll also pass it an object containing additional methods that I want it to add to its prototype."

          http://developer.yahoo.com/yui/theater/video.php?v=crockonjs-3

          Ich habe leider im Moment nicht die Zeit mir dieses Video anzusehen, auch wenn der von dir zitierte Text auf eine unterhaltsame Präsentation schließen lässt.

          Und das:

          "You never need to use new Object() in JavaScript. Use the object literal {} instead."

          Was ich auch an keiner Stelle mache.

          Falls du das auf das new chart() beziehst:
          Soweit ich weiß _muss_ ich um eine Instanz eines Objekts anzulegen das new-Schlüsselwort verwenden, andernfalls würde nur die Funktion ausgeführt.

          Vielen Dank für die Links, ich werde mich auf der Seite mal etwas weiter umschauen.

          ~dave

          1. Hallo,

            Falls du das auf das new chart() beziehst:
            Soweit ich weiß _muss_ ich um eine Instanz eines Objekts anzulegen das new-Schlüsselwort verwenden, andernfalls würde nur die Funktion ausgeführt.

            Genau. Diesen "Missstand" kritisiert er wohl auch. Javascript Object Notation wäre wohl die Alternative, wenn ich ihn richtig verstehe:

            myObject = {
             var irgendwas : function () {
                alert("hallo");
              }
             var etc : "ppp";
            }

            Gruß

            jobo

            1. Genau. Diesen "Missstand" kritisiert er wohl auch. Javascript Object Notation wäre wohl die Alternative, wenn ich ihn richtig verstehe:

              myObject = {
              var irgendwas : function () {
                  alert("hallo");
                }
              var etc : "ppp";
              }

              Das erzeugt ein neues Objekt, wenn man eine Konstruktorfunktion braucht hilft das aber nicht weiter, wie Crockford auch weiter unten schreibt:

              So the rule is simple: The only time we should use the new operator is to invoke a pseudoclassical Constructor function. When calling a Constructor function, the use of new is mandatory.

              Struppi.

    2. zum einen sagt Douglas Crockford in einem seiner Vorträge beim yahoo-theater oder wo das war, dass man "new" immer vermeiden kann und sollte, weil das nämlich irgendwie eine Inkonsitenz oder Fehlerquelle innerhalb der Sprache ist.

      Er plädiert halt für radikale funktionale und prototypische OOP. Also keine Konstruktoren (im Sinne von »new [Funktionsobjekt]«), keine Instanzen, sondern nur Objekte, die per Prototyp-Verweis aneinander delegieren. Der Schlüssel dazu ist Object.create(). Genauer erklärt er das unter »functional inheritance« in seinem Buch JavaScript: The Good Parts.

      So richtig durchgesetzt hat sich das aber (noch) nicht. Es ist den meisten Programmierern uneinsichtig, weil sie klassenbasierte OOP gewöhnt sind, wo strikt zwischen Klassendeklarationen und Instanzobjekten unterschieden wird. Da Object.create() jedoch in ECMAScript 5 eingebaut sein wird (zusammen mit Eigenschafts-Deskriptoren), hat das durchaus Potenzial. Außerdem erlauben einige JavaScript-Engines die direkte Manipulation des Prototyp-Verweises __prototype__, das macht Delegation noch einfacher.

      Mathias

  2. ich hab ein Problem bei der Vererbung in Javascript.

    var chart = function(target){
    this.ctx = target.getContext("2d");
    }

    var pieChart = function(target,config){
    this.constructor(target);
    //Verarbeite config etc.
    }

    pieChart.prototype = new chart();

    
    >   
    > In der letzten Zeile erfolgt die Vererbung.  
      
    Genau über dieses Problem hatte ich schon auf meinem Blog angefangen zu schreiben, bin aber noch nicht so weit gekommen, meine Lösung zu präsentieren.  
      
    
    > Gibt es einen Weg zu vererben ohne den Konstruktor der Elternklasse direkt aufzurufen?  
      
    Nicht einen direkten. Ein halbwegs brauchbaren Ansatz (es fehlt die z.b. Mehrfachvererbung) von mir sieht so aus:  
      
    ~~~javascript
    Function.prototype.Extend = function(b) {  
        var o = this;  
        var proto = b.prototype || b;  
      
        function Chain() {}  
        Chain.prototype = proto;  
      
        function Class() {}  
        Class.prototype = new Chain;  
      
        // inherit the prototype of this Function  
        for(var f in o.prototype) {  
            Class.prototype[f] = o.prototype[f];  
            if(typeof Class.prototype[f] == 'function') Class.prototype[f]._super = proto[f];  
        }  
        o.prototype = Class.prototype;  
        o.prototype.constructor = o;  
        o.prototype._parent = b;  
      
        Class.prototype._super = function() {  
    	var c = Class.prototype._super.caller;  
            if(!c._super && this._parent) { // Konstruktorcall?  
                var parent = this._parent, ret;  
                var proto = o.prototype;  
      
                this._parent = this._parent.prototype._parent;  
                ret = parent.apply(this, arguments);  
                this._parent = parent;  
                return ret;  
    		} else if(!c._super) throw new Error('Function has no _super function.\n' + c +'');  
            return c._super.apply(this, arguments)  
        }  
    };  
      
    var chart = function(target){  
            this.ctx = target.getContext("2d");  
    }  
      
    var pieChart = function(target,config){  
            this._super(target);  
            //Verarbeite config etc.  
    }  
      
    pieChart.Extend(chart);  
    
    

    Wenn du keine prototypen benutzt, dann reicht aber auch das aus:

      
    var chart = function(target){  
            this.ctx = target.getContext("2d");  
    }  
      
    var pieChart = function(target,config){  
            chart.call(this, target);  
            //Verarbeite config etc.  
    }  
      
    
    

    Struppi.

    1. Hi,

      vielen Dank für deine Lösung.

      Die Idee einfach ein neues Objekt anzulegen das den prototypen bekommt und bei diesem dann new aufzurufen ist gut und werde ich etwas abgewandelt übernehmen.

      ~dave

  3. Gibt es einen Weg zu vererben ohne den Konstruktor der Elternklasse direkt aufzurufen?

    Bei deinem (gekürzten?) Beispielcode macht eine »Vererbung« (bzw. Delegation, wie es in JavaScript letztlich umgesetzt ist) keinen Sinn. Was soll vererbt werden? Vermutlich die Eigenschaften des Prototype von chart, also chart.prototype. Was man üblicherweise tut ist folgendes:

    1. Erzeuge ein Objekt, dass chart.prototype als Prototyp hat (die interne [[Prototype]]-Eigenschaft zeigt auf chart.prototype).
    2. Nutze dieses Objekt als pieChart.prototype (und fülle es mit Eigenschaften und Methoden).
    3. Rufe im Konstruktor pieChart den Konstruktor chart auf.

    Implementierung:

    1. Dafür gibt es in ECMAScript 5 die Methode Object.create(). Bis die alle Browser unterstützen, hilft man sich ab:

    function Object_create (o) {  
      var F = function() {};  
      F.prototype = o;  
      return new F();  
    }
    

    Anwendung:
    Object_create(chart.prototype)
    erzeugt ein Objekt, das Anfragen an unbekannte Eigenschaften an chart.prototype delegiert.

    2. Zuweisung an pieChart.prototype:
    pieChart.prototype = Object_create(chart.prototype);

    Jetzt muss man noch den constructor-Verweis korrigieren, denn der wird mit dem Überschreiben von prototype mit überschrieben:
    pieChart.prototype.constructor = pieChart;

    Wenn man will, kann man jetzt noch einen Verweis auf den Super-Konstruktor bzw. den Super-Prototyp erzeugen.

    3. Wende manuell den chart-Konstruktor auf die pieChart-Instanz an:

    function pieChart (target, config) {  
      chart.call(this, target);  
      // ...  
    }
    

    Das mag ungewohnt aussehen, aber JavaScript ist eine funktionale und prototypische, keine klassenbasierte Sprache, bei der es native Super-Calls gibt. Man kann mit viel Metaprogramming eine Pseudoklassen-Abstraktion erzeugen - das tun Bibliotheken wie Mootools, Prototype, Dojo usw. Das sieht dann schöner aus, dafür ist es unter der Haube sehr kompliziert.

    Hintergründe:
    http://molily.de/js/organisation-instanzen.html
    http://molily.de/javascript-core/

    Mathias