MB: Warum bei Prototype-Inheritance die Konstruktor-Funktion von Parentclass zu Childclass wechseln???

moinsen,

ein Auszug aus Object.create() was mich sehr stutzig macht.

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;.

//...

es geht mir um die Zuweisung Rectangle.prototype.constructor = Rectangle. Was macht es für einen Unterschied ob die Konstruktor-Funktion Shape oder Rectangle enthält. Genau das macht meines Wissens nacht die letzte Zuweisung von Shape nach Rectangle zu wechseln. Warum?

lgmb

--
Sprachstörung

akzeptierte Antworten

  1. Hallo MB,

    es ist einfach im Javascript Objektsystem üblich, dass die Identität

    Class.prototype.constructor == Class

    erfüllt ist.

    Das liegt vor allem daran, dass es Klassen in JavaScript eigentlich gar nicht gibt, nur Prototypen. D.h. die ursprüngliche Idee war vermutlich, dass man die Konstruktorfunktion gar nicht kennt, sondern nur das Prototypobjekt. Und wenn Du dann ein neues Objekt zu einem Prototypen erzeugen willst, musst Du

    let rect = new rectProto.constructor(x,y,w,h)

    aufrufen. Und das geht natürlich nur, wenn sich dort nicht der Konstruktor des Shape-Prototypen befindet.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. moin,

      es ist einfach im Javascript Objektsystem üblich, dass die Identität […] erfüllt ist.

      Ah vielen herzlichen Dank, jetzt wird mir einiges klar!!!

      let rect = new rectProto.constructor(x,y,w,h) aufrufen. Und das geht natürlich nur, wenn sich dort nicht der Konstruktor des Shape-Prototypen befindet.

      Ist verständlich! Danke Dir!!!

      Ich weis über Tutoren, dass JS nicht OOP kann wie Java sondern eben Prototypen-Vererbung. Ich weis auch das Object.create( Foobar ) das gleiche macht wie new Foobar nur eben ohne Übergabeargumente. Das ist vorteilhaft für new.

      Gibt es einen Weg ein Objekt zu erzeugen mit Übergabewerten ohne den new-Operator???

      Ich kann mein angesprochenes Problem nur sequenziell lösen (über mehrere Zuweisungs-Befehle) wenn man Übergabeparameter haben will:

      function Foo() {}
      
      let f = Object.create( Foo );
      f.fuz = '123';
      f.buz = '456';
      

      Meine Frage:

      • Gibt es einen besseren Weg z.B. Object.create( <Object>, [<params>] )
      • warum verwendet man nicht den new-Operator? gibt's da einen entscheidenden Performance Grund???

      lgmb

      --
      Sprachstörung
      1. Hallo MB,

        also wenn mit Object.create, dann so: Object.create(Foo.prototype).

        Aber warum kannst Du den Konstruktor nicht aufrufen? Das geht auch, wenn das Funktionsobjekt in einer Variablen steckt.

        function createShape(ctor, x, y) {
           return ctor(x,y);
        }
        
        let rect = createShape(Rectangle, 100, 200);
        

        Rolf

        --
        sumpsi - posui - obstruxi
        1. moin,

          Aber warum kannst Du den Konstruktor nicht aufrufen? Das geht auch, wenn das Funktionsobjekt in einer Variablen steckt.

          Sry, erläutere mir genauer: wo ist die Konstruktor Funktion die den ctor-Parameter befüllt? Meinst du das in einer Funktion???

          // Konstruktor
          const Foo = function() {};
          Foo.prototype.bar = '<default>';
          Foo.prototype.qax = '<default>';
          
          // Erzeuger mit Übergabewerten
          const ctor = ( ctor, params ) => {
            let instanz = Object.create(ctor.prototype);
            params.forEach( param => instanz[ param.name ] = param.value );
            return instanz;
          };
          
          // Aufruf des Erzeugers
          const f = ctor( Foo, [
            { name: 'bar', value: 'Hello World' }
          ] );
          

          Tests

          f.bar // 'Hello World'
          
          f.qax // '<default>'
          
          f.tok // undefined
          

          Hab's gerade in FF getestet und es geht.

          Welche BuiltIn Kostruktor Funktion, die nicht selbst geschrieben istohgne new operator, kann man verwendet um einen Instanz mit übergabewerten zu erzeugen?

          Ich weis, dass nur sequenziell sowas geht...

          const Foo = function() {};
          Foo.prototype.bar = '<default>';
          Foo.prototype.baz = '<default>';
          let f = Object.create(Foo.prototype);
          f.bar = 'Hello World';
          // 'Hello World'
          f.baz;
          // '<default>'
          

          Was nicht geht is sowas...

          const f = Object.crerate( Foo.prototype. { bar: 'Hello World' } );
          

          bar soll doch bitte n objekt sein und keine String-Property.

          Mir ist bekannt das in JS der new Operator ungern gesehen wird, da es ja nicht OO sondern OB ist.

          Frist der new-Operator mehr performance, wenn man ihn nicht den verwendet statt dessen den Object.create()-Funktion und dann mit dem erzeugten Objekt protoypen setzt, genau wie bei der Objekt-Instanziierung???

          lgmb

          --
          Sprachstörung
          1. Hallo MB,

            ich komme bei deinen Rückfragen nicht so ganz mit. Kann dran liegen, dass das Thema 5 Tage alt ist 😉

            Deine Frage war doch:

            Gibt es einen Weg ein Objekt zu erzeugen mit Übergabewerten ohne den new-Operator???

            Und ich hab nicht verstanden, warum das ein Problem sein sollte. Das einzige, was ich mir vorstellen konnte, war, dass Du einen Fall hast, wo Du nur den Prototyp kennst (oder eine existierende Objektinstanz), aber nicht die Konstruktorfunktion direkt.

            Dein Object.create(Foo) hilft da beim Verständnis auch nicht weiter, denn der Parameter für Object.create ist das Prototypobjekt, das für das neue Objekt verwendet werden soll. Bei Dir ist Foo aber eine Funktion.

            Jetzt stellst Du eine andere Behauptung auf:

            Mir ist bekannt das in JS der new Operator ungern gesehen wird, da es ja nicht OO sondern OB ist.

            Aha. Soso. Mir ist das nicht bekannt. Mir ist auch nicht bekannt, was "OB" ist. Aber ich bin auch kein JavaScriptpapst, nur ein einfacher Programmierer. Welche Quelle hast Du da? Wenn ich nach OO OB googele oder bingele, finde ich nur irgend so einen ollen Jedimeister. Nicht hilfreich.

            Der new Operator ist gerade dafür gemacht, um die klassenorientierte OO in JavaScript nachzubilden. Er ist ideal für den Fall, dass man Objekte mit Prototyp anlegen und mit Hilfe einer Funktion initialisieren möchte. Einen Performance-Nachteil hat er nicht.

            Deine "Erzeuger" Funktion behandelt einen Sonderfall, nämlich, dass man extrem generisch über eine Key-Value Liste Objekte initialisieren können möchte. Auf diese Weise fängt man sich schon einen Performance-Nachteil ein. Aber selbst der fällt nicht auf, wenn man lediglich einige Objekte auf diese Weise anlegt. Nur wenn man Code hat, der in kurzer Zeit hunderte oder tausende von Objekten erzeugen muss, kann das weh tun.

            Was man vermeiden sollte, ist, in der Konstruktorfunktion Methoden an this zuzuweisen. Denn das ist Käse. Für jede dieser Zuweisungen muss JS ein neues Funktionsobjekt anlegen und der Aufrufkontext der Konstruktorfunktion klebt als Closure noch dran fest. Methoden sollte man nur am Prototypen haben und in die Objekte hineinvererben. Gut - ich hatte schon Fälle, wo ich im Konstruktor Methoden zugewiesen habe, einfach um den Kontext der Konstruktorfunktion als privaten Datenspeicher zu haben. Aber dafür gibt's mittlerweile auch die private Features der class Syntax (etwas, was sich anders gar nicht realisieren lässt)

            Rolf

            --
            sumpsi - posui - obstruxi
            1. moin,

              ich komme bei deinen Rückfragen nicht so ganz mit. Kann dran liegen, dass das Thema 5 Tage alt ist 😉

              Sry, ich war mit der Stellung der Frage hin und her gerissen: Neuer Thread oder unter deiner Antwort posten 🤔. Ich hab mich für's zweite enschieden.

              Jetzt stellst Du eine andere Behauptung auf:

              Mir ist bekannt das in JS der new Operator ungern gesehen wird, da es ja nicht OO sondern OB ist.

              Aha. Soso. Mir ist das nicht bekannt. Mir ist auch nicht bekannt, was "OB" ist. Aber ich bin auch kein JavaScriptpapst, nur ein einfacher Programmierer. Welche Quelle hast Du da? Wenn ich nach OO OB googele oder bingele, finde ich nur irgend so einen ollen Jedimeister. Nicht hilfreich.

              Sry 😅, ich bin im Abbreviation-Wahn 😔. Ich dachte OO liest sich leichter als 'Object oriented' und OB für 'Object based'. Jedenfalls gehts mir so und man macht weniger Fehler bei zwei Buchstaben. Sry dafür wenn der Schuss nach hinten los ging 😕.

              Der new Operator ist gerade dafür gemacht, um die klassenorientierte OO in JavaScript nachzubilden. Er ist ideal für den Fall, dass man Objekte mit Prototyp anlegen und mit Hilfe einer Funktion initialisieren möchte. Einen Performance-Nachteil hat er nicht.

              Ein Tutor von Udemy sprach von Syntactical Sugar wo sich Objektorientierte-Programmierer wohl in der Klassen-Syntax heimischer fühlen würden. Das könne man mit objektbasierender weise eleganter, besser und performanter hinkriegen. Das habe ich so verstanden und interpretiert. Ich schlussfolgerte, dass ich wohl beim lernen von Prototypen-Vererbung bleibe.

              Deine "Erzeuger" Funktion behandelt einen Sonderfall, nämlich, dass man extrem generisch über eine Key-Value Liste Objekte initialisieren können möchte. Auf diese Weise fängt man sich schon einen Performance-Nachteil ein.

              Aber selbst der fällt nicht auf, wenn man lediglich einige Objekte auf diese Weise anlegt. Nur wenn man Code hat, der in kurzer Zeit hunderte oder tausende von Objekten erzeugen muss, kann das weh tun.

              ich hab's befürchtet, dass das n Performance-Problemchen werden würde, wenn man tausende Objekte erzeugt. Die new-Funktion schneidet besser ab?

              Was man vermeiden sollte, ist, in der Konstruktorfunktion Methoden an this zuzuweisen.

              Das ist mir neu. Ich dachte, jede Prototyp-Funktion eines Konstruktors schleppen auch immer this-objekt mit Referenz auf die Instanz mit sich rum oder nicht? In dem Fall wäre es doch Jacke wie Hose?

              Ich hoffe wir reden nicht aneinander vorbei 😕. Daher will ich das am Beispiel veranschaulichen…

              Bsp. 1.

              const Foo = function( bar ) {
                this.bar = bar;
              };
              Foo.prototype.getBar = function(){
                return this.bar;
              }
              

              oder

              Bsp. 2.

              const Foo = function( bar ) {};
              Foo.prototype.getBar = function(){}
              

              Beide Beispiele enthalten doch this-Objekte. So mein Verständnis eines noch-nicht-javascript-entwicklers 🤔.

              lgmb

              PS.: Ich bin Agnostiker! Papst hin oder her, wenn auch nur JavaScriptPapst 🤪.

              --
              Sprachstörung
              1. Hallo MB,

                "objektorientiert" und "objektbasierend" - uff, das musste ich jetzt erstmal nachschlagen. Demnach ist "objektbasierende" Programmierung, wenn Du mit den Objekten hantierst, die JavaScript und Browser mitbringen, aber keine eigenen erstellst.

                Sobald Du deinen Code selbst in Objekte steckst und sogar Vererbung verwendest, bist Du objektorientiert. Sagt Tante Wiki. Demnach ist es für OO vs OB total wurscht, ob Du new Foo() oder Object.create(Foo.prototype) verwendest. Es ist beides OO.

                Der new Operator und das class Schlüsselwort - da hat der Tutor recht, das ist Syntaxzucker. Er ist aber lecker. Diese beiden JS-Beispiele tun äußerlich exakt das Gleiche:

                let a = new Foo("bar", 42);
                

                vs

                let a = Object.create(Foo.prototype);
                
                Foo.call(a, "bar", 42);
                // oder
                Foo.apply(a, [ "bar", 42 ] );
                

                Der Zucker enthält aber auch ein paar Körnchen Salz (das hebt bekanntlich den Geschmack). Gerade bei mehrstufiger Vererbung hast Du viel Handarbeit, die Dir die class-Syntax und der new Operator abnehmen.

                class Parent {
                   constructor(x) {
                      this.x = x;
                   }
                   foo(t) {
                      return t + this.x;
                   }
                }
                
                class Child extends Parent {
                   constructor(x,q) {
                      super(x);
                      this.q = q;
                   }
                   bar(u) {
                      return u * this.q + this.x;
                   }
                }
                
                let c = new Child(42, 17);
                

                Das ist ohne class Syntax eine Menge mehr Arbeit. Vor allem musst Du die Programmiermuster exakt einhalten, um die Objekte sauber hinzubekommen, und ich kann mir nicht vorstellen, dass von Hand erstellte prototypische Vererbung - außer in Sonderfällen - performanter ist. Und wenn, dann höchstens um ein paar Nanosekunden.

                Prototypen haben den Vorteil, dass man mal eben so ein Objekt als Vererbungsgrundlage nehmen kann, ohne Klassen definieren zu müssen. Das ermöglicht interessante Techniken zur Komposition von Bausteinen zur Laufzeit, die mit Klassen deutlich umständlicher sind (falls Du das Entwurfsmuster-Buch der Gang of Four mal gelesen hat: denke an das Dekorierer-Muster).

                Das schöne an JS ist aber auch, dass Du fallweise entscheiden kannst, ob Du eine Klasse notierst oder mit Object.create arbeitest, so dass Du nach Bedarf die bessere Lösung auswählen kannst.

                ich hab's befürchtet, dass das n Performance-Problemchen werden würde, wenn man tausende Objekte erzeugt. Die new-Funktion schneidet besser ab?

                Nein, das ist kein Problem von new. Sondern davon, wieviel Aufwand Du in die Erzeugung eines Objekts steckst. Deswegen schrieb ich ja, dass man im Konstruktor (oder bei der Objektinitialisierung im Prototyp-Verfahren) keine Methoden an das neue Objekt zuweisen soll, sondern Methoden nach aller Möglichkeit vom Prototypen erben soll.

                Um bei deinem Abschlussbeispiel zu bleiben:

                const Foo = function(bar) {
                   this.bar = bar;
                }
                Foo.prototype.getBar = function() { return this.bar; }
                

                ist so okay. Der getter wird vom Prototypen geerbt. Man könnte es aber auch so machen:

                const Foo = function(bar) {
                   let _bar = bar
                   this.getBar = function() { return _bar; }
                   this.setBar = function(b) { _bar = b; }
                }
                

                Auf den ersten Blick ist das großartig: Ich habe private Eigenschaften eines Projekts erfunden! Ich kann auf _bar nur über getter und setter zugreifen. Aber selbst dieser simple Konstruktor läuft doppelt so lange wie dies hier:

                class Foo {
                   #bar;
                   get bar() { return this.#bar; }
                   set bar(b) { this.+bar = b; }
                }
                

                Das ist die volle, zahnerweichende Zuckerdröhnung: class-Syntax, private Property, getter und setter. Und spätestens bei private-Eigenschaften (das # vor dem bar) ist die Class-syntax unverzichtbar, denn die sind anderweit nicht verfügbar, bestenfalls unvollständig simulierbar.

                Und wenn Du die echte Property-Syntax ohne private properties simulieren willst, geht's auf Faktor 10 hoch:

                function Foo() {
                   let bar;
                   Object.defineProperties(this,  {
                      bar: { get: function() { return bar; },
                             set: function(b) { bar = b; } } } );
                }
                

                Kann man so machen, erzeugt auch zuverlässig ein Property 'bar' mit einem privaten Backing-Feld - aber generiert auch eben zwei Funktionskontexte pro Konstruktoraufruf, was Zeit und Speicher kostet.

                Wobei "doppelt so lange" ebenfalls mit einer Prise Salz zu genießen ist: Es sind auf meinem PC 300 Nanosekunden mit Class-Syntax, was dann auf 600 Nanosekunden oder 3 Mikrosekunden steigt. Man muss schon ziemlich viele Objekte erzeugen, bevor das weh tut.

                Rolf

                --
                sumpsi - posui - obstruxi
  2. Hello,

    ich sitze hier auf Teathering... Darum nur kurz:

    Kann man diese ganzen Denglish-Flachbegriffe eigentlich auch noch in kurzen Sätzen ins Deutsche übersetzen? Unabhängig davon, ob "Fachsprache" oder nicht, würde das mMn helfen, das Grundverständnis zu fördern.

    Eure Meinung?

    Glück Auf
    Tom vom Berg

    --
    Es gibt nichts Gutes, außer man tut es!
    Das Leben selbst ist der Sinn.
    1. Hallo TS,

      Teathering

      Was muss ich mir darunter vorstellen? Einen Hering mit Zitzen (Teat Hering)? Brrr - diese Bilder - DELETE DELETE DELETE 😉

      Ach, tethering…

      Um die Begriffe zu "übersetzen" müsste ich Dir eine OOP Vorlesung halten. Der einzige Begriff, der unnötig englisch war, ist aus meiner Sicht "Inheritance" im Thread-Titel, wo man hätte "Vererbung" hätte schreiben können. Der Rest ist aus meiner Sicht die übliche Fachsprache und hat die Herabwürdigung als Flachsprache nicht verdient.

      Eine Einführung in die Vererbungslehre von JavaScript findest Du übrigens im Wiki. Wenn Du Fragen hast, können wir uns gern am Stammtisch zusammensetzen und drüber reden.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Hello,

        Teathering

        Was muss ich mir darunter vorstellen? Einen Hering mit Zitzen (Teat Hering)? Brrr - diese Bilder - DELETE DELETE DELETE 😉

        Ach, tethering…

        Nee, ich hatte Tee dazu. Bin ja schließlich an der Grenze des Internets (Nordfriesland) in einer DJH.

        :-P

        Glück Auf
        Tom vom Berg

        --
        Es gibt nichts Gutes, außer man tut es!
        Das Leben selbst ist der Sinn.