Siri: Aufbau von Funktionen/Objekten. Verständnisfrage

Hallo,

ich hab Molilys überaus interessante "Einführung in JavaScript" gelesen und mich damit auseinander gesetzt. Alles habe ich aber auch nach mehrmaligem lesen nicht verstanden.

Variante A:

function Konstruktor1 () {  
  
  //privat  
  k1var1 = "test1";  
  // öffentlich  
  this.k1var2 = "test2";  
  
  //privat  
  k1methode1 = function() {  
    alert (k1var1+"-"+this.k1var2);  
  }  
  //öffentlich  
  this.k1methode2 = function() {  
    alert (k1var1+"-"+this.k1var2);  
  }  
}  
var instanz1 = new Konstuktor1();  
instanz1.k1methode2(); // geht  
instanz1.k1methode1(); // geht nicht

Variante B:

function Konstruktor2 () {  
  
  //privat  
  var k2var1 = "test3";  
  // öffentlich  
  k2var2 = "test4";  
  
  //privat  
  function k2methode1() {  
    alert (k2var1);  
  }  
  //öffentlich  
  return {  
    k2methode2: function() {  
      alert (k2var1);  
    }  
  }  
}  
var instanz2 = new Konstuktor2();  
instanz2.k2methode2(); // geht  
instanz2.k2methode1(); // geht nicht

Beide Schreibweisen erlauben die Deklaration von privaten und öffentlichen Variablen/Methoden. Wo liegt der Unterschied? Ist es eine Frage des persönlichen Stils? Oder gibt es bestimmte Einsatzzwecke für die eine der Varianten besser geeignet ist als die andere?

Grüße
Siri

  1. Hallo,

    Beide Schreibweisen erlauben die Deklaration von privaten und öffentlichen Variablen/Methoden. Wo liegt der Unterschied?

    Normalerweise gibt ein Konstruktor nichts explizit mit »return« zurück, denn »new Konstruktor« gibt automatisch das damit neu erzeugte Instanzobjekt zurück.

    Ein Konstruktor *kann* ein anderes Objekt als die Instanz zurückgeben, i.d.R. besteht dazu aber kein Grund. Die zweite Variante ist daher unüblich. Wahrscheinlich verwechselst du sie mit dem Module-Pattern oder rein funktionaler Erzeugung von Objekten (letztere behandle ich in meiner Einführung nicht). Dann würde man aber nicht mit »new« arbeiten:

    function createFoo () {  
      function privateFunc () {}  
      return {  
        func: function () {  
          privateFunc();  
        }  
      };  
    }  
      
    var foo = createFoo();  
    foo.func();
    

    Manche propagieren einen solchen funktionalen Stil, weil er verschiedene Vorteile hat (siehe dazu Douglas Crockfords »JavaScript: The Good Parts«).

    Bei einem normalen Konstruktor, der mit new aufgerufen wird, gibt es i.d.R. kein return-Statement. Man greift darin über »this« auf das bereits erzeugte Instanzobjekt zurück.

    Mathias

    1. Hallo,

      vielen Dank für die Erläuterung!

      function createFoo () {

      function privateFunc () {}
        return {
          func: function () {
            privateFunc();
          }
        };
      }

      var foo = createFoo();
      foo.func();

        
      Könnte man also sagen, dass die obige Variante "statisch" ist?  
        
      Grüße  
      Siri  
      
      
      1. function createFoo () {

        function privateFunc () {}
          return {
            func: function () {
              privateFunc();
            }
          };
        }

        var foo = createFoo();
        foo.func();

        
        >   
        > Könnte man also sagen, dass die obige Variante "statisch" ist?  
          
        Inwiefern statisch? Was bedeutet das? Im Gegensatz zu dynamisch?  
          
        Mathias
        
        -- 
        [Chaplin - An Application Architecture Using Backbone.js](https://github.com/chaplinjs/chaplin)
        
        1. Hallo,

          Könnte man also sagen, dass die obige Variante "statisch" ist?

          Inwiefern statisch? Was bedeutet das? Im Gegensatz zu dynamisch?

          Wenn man einen hinkenden Vergleich zu Java ziehen möchte. Wenn ich also die Methoden einer Klasse statisch aufrufe, ohne vorher ein Objekt erzeugt zu haben.

          Grüße
          Siri

          1. Wenn man einen hinkenden Vergleich zu Java ziehen möchte. Wenn ich also die Methoden einer Klasse statisch aufrufe, ohne vorher ein Objekt erzeugt zu haben.

            Dieses Wort verwendet man in diesem Zusammenhang in JavaScript nicht. Es handelt sich um einfache Objekte mit Funktionen als Eigenschaften. Der Vergleich zu Klassen mit statischen Methoden passt da vorne und hinten nicht. Es *wird* ja ein Objekt erzeugt und eine Funktion daran gehängt.

            Das Konzept wäre eher auf einfache Objekt-Literale anwendbar:

            var Foo = {  
              func: function () {}  
            };
            

            In meiner Einführung vergleiche ich das aber eher mit Singletons, weil das Objekt einen Status haben kann.

            Wenn man in JS von statischen Eigenschaften spricht, meint man meist Eigenschaften des Konstruktors selbst. Der ist ja auch nur ein Funktionsobjekt, das Eigenschaften haben kann.

            function Konstruktor () {}  
            Konstruktor.statischeEigenschaft = 'foo';
            

            Aber das ist terminologisch nicht so sauber. In JS gibt es einfach Objekte, und Funktionen sind Objekte erster Klasse. Was da eher herausfällt ist das Aufrufen von Funktionen mittels »new«.

            Mathias

  2. gruss Siri,

    Beide Schreibweisen erlauben die Deklaration von privaten und öffentlichen
    Variablen/Methoden. Wo liegt der Unterschied? ...
    ...

    Variante A ist eine Konstruktorfunktion, die über den [new] Operator und den
    den Bezug auf [this] Instanzen ihres eigenen Typs erstellt.

    (new Konstruktor1) instanceof Konstruktor1 ist in jedem Fall wahr.

    ...
    Variante A:

    function Konstruktor1 () {

    //privat
      k1var1 = "test1";

    /*
        ganz böse ... die Zuweisung von "test1" auf [k1var1] erfolgt im globalen Namensraum.
        Nur ein vorangestelltes [var] zwingt [k1var1] in den lokalen Scope der Funktion [Konstruktor1].
    /
        //local
        var k1var1 = "test1";
    /

        Kapselung wird durch den lokalen Scope der Variable und dem Aufruf von [Konstruktor1] erreicht.
        Siehe dazu molilys Erläuterungen zu [Closure]s.
    */

    // öffentlich
      this.k1var2 = "test2";

    /*
        Richtig. [this] bindet die Eigenschaft [k1var2] direkt adressierbar an jede Instanz der
        Konstruktor-Funktion [Konstruktor1].
    */

    //privat
      k1methode1 = function() {
        alert (k1var1+"-"+this.k1var2);
      }

    /*
        nein - keineswegs privat, sondern global - siehe Erklärung weiter oben und Korrektur direkt darunter.
    */
        var k1methode1 = function() {
          alert (k1var1+"-"+this.k1var2);
        }

    //öffentlich
      this.k1methode2 = function() {
        alert (k1var1+"-"+this.k1var2);
      }

    /*
        jo.
    */

    }
    var instanz1 = new Konstuktor1();
    instanz1.k1methode2(); // geht

    /*
        jepp
    */

    instanz1.k1methode1(); // geht nicht

    /*
        richtig - [k1methode1] wurde ja auch im globalen Namensraum angelegt
    /
        window.k1methode1();
    /

        ... sollte aufrufbar sein und "test1-undefined" liefern ...
        ... warum? ...

    - [k1var1] wurde ebenfall global angelegt und lässt sich nach "test1" auflösen.
        - [k1var2] hingegen existiert nicht im globalen namensraum, ...
        - ... denn genau dorthin wird [this.k1var2] aus der global angelegten [k1methode1] aufgelöst.
    */

    
    > ...  
      
      
    Die Methode [Konstruktor2] ist keine Konstruktor-Funktion, obwohl der gemeinsame Aufruf von  
    [new] Operator und [Konstruktor2] aus dem Beispiel der Variante B dies so erscheinen läßt.  
      
    `(new Konstruktor2) instanceof Konstruktor2`{:.language-javascript} wird niemals wahr sein.  
      
    Warum?  
      
    [Konstruktor2] gibt ein durch ein Objekt-Literal erzeugtes Objekt zurück. Dieses Objekt ist  
    somit keine Instanz von [Konstruktor2]. Als einzige Eigenschaft besitzt dieses Objekt die  
    Methode [k2methode2], die nur deshalb Zugriff auf die im lokalen Funktions-Scope von [Konstruktor2]  
    vereinbarte Variable [k2var1] hat, da das zurückgegebene Objekt im selben Scope erzeugt wird.  
      
    Siehe dazu wiederum molilys Erläuterungen zu [Closure]s.  
      
      
    
    > ...  
    > Variante B:  
    > ~~~javascript
    
    function Konstruktor2 () {  
    
    >   
    >   //privat  
    >   var k2var1 = "test3";  
    
    /*  
        jepp - aber trotzdem bitte "lokal" statt "privat" denken und schreiben.  
    */  
    
    >   // öffentlich  
    >   k2var2 = "test4";  
    
    /*  
        nope - [k2var2] liegt im globalen Namensraum - siehe Erläuterungen zu Beispiel A.  
    */  
    
    >   
    >   //privat  
    >   function k2methode1() {  
    >     alert (k2var1);  
    >   }  
    
    /*  
        jepp  
    */  
    
    >   //öffentlich  
    >   return {  
    >     k2methode2: function() {  
    >       alert (k2var1);  
    >     }  
    >   }  
    
    /*  
        siehe Erklärung zu  Beispiel B.  
    */  
    
    > }  
    > var instanz2 = new Konstuktor2();  
    > instanz2.k2methode2(); // geht  
    
    /*  
        aber nur wegen [link:http://molily.de/js/organisation-module.html#revealing-module@title=Revealing Module Pattern]  
    */  
    
    > instanz2.k2methode1(); // geht nicht  
    
    /*  
        richtig - [k2methode1] wurde ja auch im globalen Namensraum angelegt  
    */  
    
    > ...  
    
    

    ... Ist es eine Frage des persönlichen Stils? Oder gibt es bestimmte Einsatzzwecke für die eine
    der Varianten besser geeignet ist als die andere?

    Benutze Konstruktoren, wenn Du ein Typsystem erstellen möchtest, wo Du die Herkunft von Objekten
    anhand des [instanceof] Operators bestimmen möchtest. bzw. wenn Deinen Objekten Methoden über die
    Delegation durch [Constructor.prototype] zugewiesen werden sollen.

    so long - peterS. - pseliger@gmx.net

    --
    »Because objects in JavaScript are so flexible, you will want to think differently about class hierarchies.
    Deep hierarchies are inappropriate. Shallow hierarchies are efficient and expressive.« - Douglas Crockford
    ie:( fl:) br:> va:( ls:& fo:) rl:) n3;} n4:} ss:} de:µ js:} mo:? zu:]
    1. Hallo,

      vielen Dank!

      Schwerer Tobak... Da muss ich wohl noch mehr in die Praxis gehen, aber ich denke ich hab jetzt die Grundzüge besser verstanden.

      Grüsse
      Siri

      1. hallo again Siri

        Schwerer Tobak... Da muss ich wohl noch mehr in die Praxis gehen, ...

        dann lass doch mal zur Veranschaulichung den folgenden auf das Minimum
        reduzierte Code zeilenweise auf eine JavaScript-Konsole los.

          
        var x = function () {var a="a";return {a:a};};  // undefined  
          
        x();                                            // Object  -> a: "a"  
        x() instanceof x                                // false  
        new x();                                        // Object  -> a: "a"  
        new x() instanceof x                            // false  
          
          
        var x = function () {var a="a"; this.a=a;};     // undefined  
          
        x();                                            // undefined  
        x() instanceof x                                // false  
        new x();                                        // x  -> a: "a"  
        new x() instanceof x                            // true  
          
        
        

        ... aber ich denke ich hab jetzt die Grundzüge besser verstanden.

        so long - peterS. - pseliger@gmx.net

        --
        »Because objects in JavaScript are so flexible, you will want to think differently about class hierarchies.
        Deep hierarchies are inappropriate. Shallow hierarchies are efficient and expressive.« - Douglas Crockford
        ie:( fl:) br:> va:( ls:& fo:) rl:) n3;} n4:} ss:} de:µ js:} mo:? zu:]