Ralf: JS-Bibliothek strukturieren

Moin!

Ich plane, diverse Funktionen in eine Bibliothek zu integrieren, für die ich mir folgende Struktur ausgedacht habe:

  
var lib = {  
  func1 : {  
    version : 'a.b.c', // func1 version  
    init : function () {  
    ...  
    }  
  },  
  func2 : {  
    version : 'x.y.z', // func2 version  
    init : function () {  
    ...  
    }  
  },  
  version : 'l.m.n' // lib version  
};  

Um von außen einen Zugriff auf die einzelnen Unterobjekte zu ermöglichen, ohne diese jedoch zu kennen, würde ich gern innerhalb des lib Objektes ein Unterobjekt definieren, welches die vorhandenen funcx Objekte enthält (bzw. ein Subset davon, welches ich nach außen sichtbar machen möchte).
Dafür hatte ich mir zunächst ein Array ausgedacht:

  
modules : [func1, func2],  

Dieses wollte ich als Unterobjekt von lib definieren, aber das haut nicht hin.

Wie schaffe ich es, innerhalb von lib so etwas zu definieren?

Ich fange gerade erst an, in Javascript objektorientiert zu denken und es fällt mir noch sehr schwer. Vielleicht gibt es ja einen besseren Ansatz?

Ralf

  1. Ich plane, diverse Funktionen in eine Bibliothek zu integrieren, für die ich mir folgende Struktur ausgedacht habe:

    var lib = {
      func1 : {
        version : 'a.b.c', // func1 version
        init : function () {
        ...
        }
      },
      func2 : {
        version : 'x.y.z', // func2 version
        init : function () {
        ...
        }
      },
      version : 'l.m.n' // lib version
    };

    
    > Um von außen einen Zugriff auf die einzelnen Unterobjekte zu ermöglichen, ohne diese jedoch zu kennen, würde ich gern innerhalb des lib Objektes ein Unterobjekt definieren, welches die vorhandenen funcx Objekte enthält (bzw. ein Subset davon, welches ich nach außen sichtbar machen möchte).  
      
    Deine Variante hat den Nachteil, dass du keinen gemeinsamen Scope hast, also auch keine lokale Package Variabel (um das ein bisschen Perl'isch auszudrücken) hast. Daher bevorzuge ich mittlerweile diese Variante:  
      
    ~~~javascript
    var lib = new function() {  
    this.func1 = function() { /* ...*/ };  
    this.func2 = function() { /* ...*/ };  
    var module = [];  
    for(var f in this) if(typeof this[f] == 'function') module.push(f);  
    this.module = function() { return module; };  
    }  
    alert( lib.module())  
    
    

    Struppi.

    1. Deine Variante hat den Nachteil, dass du keinen gemeinsamen Scope hast, also auch keine lokale Package Variabel (um das ein bisschen Perl'isch auszudrücken) hast. Daher bevorzuge ich mittlerweile diese Variante:

      var lib = new function() {

      this.func1 = function() { /* .../ };
      this.func2 = function() { /
      ...*/ };
      var module = [];
      for(var f in this) if(typeof this[f] == 'function') module.push(f);
      this.module = function() { return module; };
      }
      alert( lib.module())

        
      Sieht interessant aus. Dann müsste ich aber auch jegliche Werte, die ich jetzt in Unterobjekten definiert habe, in Funktionen kapseln? Und vordefinierte Werte zu überschreiben, wäre wohl dann etwas komplizierter, wenn ich das richtig erfasst habe.  
        
      Nach meinem Posting bin ich auf die Idee gekommen, die Definition doch außerhalb vorzunehmen:  
      ~~~javascript
        
      lib.modules = [lib.func1, lib.func2];  
      
      

      Ralf

      1. var lib = new function() {

        this.func1 = function() { /* .../ };
        this.func2 = function() { /
        ...*/ };
        var module = [];
        for(var f in this) if(typeof this[f] == 'function') module.push(f);
        this.module = function() { return module; };
        }
        alert( lib.module())

        
        >   
        > Sieht interessant aus. Dann müsste ich aber auch jegliche Werte, die ich jetzt in Unterobjekten definiert habe, in Funktionen kapseln? Und vordefinierte Werte zu überschreiben, wäre wohl dann etwas komplizierter, wenn ich das richtig erfasst habe.  
          
        Wenn das Skript natürlich jetzt schon umfangreicher ist, ja.  
        Du kannst auch die ganze Struktur in die Konstruktorfunktion legen.  
        ~~~javascript
        var lib = new function() {  
        var lib = {  
        func1: function() { /* ...*/ },  
        func2: function() { /* ...*/ }  
        };  
        var module = [];  
        for(var f in lib) if(typeof lib[f] == 'function') module.push(f);  
        lib.module = function() { return module; };  
        return lib;  
        }  
        
        

        Nach meinem Posting bin ich auf die Idee gekommen, die Definition doch außerhalb vorzunehmen:

        Was ich nicht für schön halte. Was ist wenn du auf die Idee kommst die Bibliothek MyLib zu nennen?

        lib.modules = [lib.func1, lib.func2];

        Dann müßtest du hier alles ersetzen. In OOP versucht man so wenig wie möglich konkret zu werden, möglichst viel Kapseln (in deinem Konstrukt sind z.b. keine lokalen Bibliotheksvariabeln möglich).

        Struppi.

        1. Du kannst auch die ganze Struktur in die Konstruktorfunktion legen.

          var lib = new function() {

          var lib = {
          func1: function() { /* .../ },
          func2: function() { /
          ...*/ }
          };
          var module = [];
          for(var f in lib) if(typeof lib[f] == 'function') module.push(f);
          lib.module = function() { return module; };
          return lib;
          }

            
          Ich weiß nicht, ob ich das verstanden habe. Wie führe ich denn dann z.B. eine Funktion aus, die sich innerhalb von func1 befindet?  
            
          
          > Was ich nicht für schön halte. Was ist wenn du auf die Idee kommst die Bibliothek MyLib zu nennen?  
          >   
          > > lib.modules = [lib.func1, lib.func2];  
          >   
          > Dann müßtest du hier alles ersetzen. In OOP versucht man so wenig wie möglich konkret zu werden, möglichst viel Kapseln (in deinem Konstrukt sind z.b. keine lokalen Bibliotheksvariabeln möglich).  
            
          Vielleicht sollte ich mal erklären, was ich erreichen will. Ich habe einige Funktionen geschrieben, die für die Seiten eines Shopsystems zusätzliche Funktionalität bereitstellen. Der Quelltext der Seiten ist nur teilweise beeinflussbar, so dass die Grundstruktur der Seiten festliegt. Javascript und CSS können aber problemlos hinzugefügt werden.  
            
          Da diese Funktionen intern einige Abläufe und Hilfsfunktionen verwenden, die sich gleichen, wollte ich den gesamten Leistungsumfang in einer Bibliothek zusammenfassen. Diese soll dann Wertedefinitionen, interne Hilfsfunktionen und die eigentlichen "Nutzfunktionen" enthalten. Letztere müssen von außen aufgerufen werden können und es muss auch möglich sein, innerhalb des Pakets vorgenommene Werte und Funktionen nachträglich zu überschreiben.  
            
          So hatte ich geplant, die Initialisierungsfunktion für func1 aus meinem Eingangsbeispiel mit lib.func1.init() aufzurufen. Auf die interne Version von func1 hätte ich über lib.func1.version Zugriff. In func1 vordefinierte Werte könnte ich mit lib.func1.wert = .... überschreiben.  
            
          Mein Beispiel mit den Modulen war dafür gedacht, dass man von außen mit einem Funktionsaufruf die Versionen aller Einzelmodule ausgeben kann. Für die Funktionalität selbst ist das eher belanglos.  
            
          Vielleicht bin ich immer noch zu sehr der hierarchischen Programmierung verhaftet und der OOP-Funke will nicht überspringen? Jahrzehnte mit anderer Denkweise lassen sich eben nicht so einfach abschütteln.
          
          1. Du kannst auch die ganze Struktur in die Konstruktorfunktion legen.

            var lib = new function() {

            var lib = {
            func1: function() { /* .../ },
            func2: function() { /
            ...*/ }
            };
            var module = [];
            for(var f in lib) if(typeof lib[f] == 'function') module.push(f);
            lib.module = function() { return module; };
            return lib;
            }

            
            >   
            > Ich weiß nicht, ob ich das verstanden habe. Wie führe ich denn dann z.B. eine Funktion aus, die sich innerhalb von func1 befindet?  
              
            genau wie du es jetzt auch machst:  
            `lib.func1();`{:.language-javascript}  
              
            
            > Vielleicht bin ich immer noch zu sehr der hierarchischen Programmierung verhaftet und der OOP-Funke will nicht überspringen? Jahrzehnte mit anderer Denkweise lassen sich eben nicht so einfach abschütteln.  
              
            Vermute ich auch.  
              
            Struppi.
            
            1. Noch eine Frage zum Abschluss: Wenn ich es richtig verstanden habe, bekomme ich diese Unterscheidung public/private nur hin, wenn ich außen  ein Funktionsobjekt definiere?

              1. Noch eine Frage zum Abschluss: Wenn ich es richtig verstanden habe, bekomme ich diese Unterscheidung public/private nur hin, wenn ich außen  ein Funktionsobjekt definiere?

                Ja, da in JS lokale Variabeln nur innerhalb einer Funktion existieren.

                Struppi.

                1. Nun habe ich doch noch eine konkrete Frage. Nehmen wir mal folgende Konstruktion an:

                    
                  var lib = new function () {  
                    this.func1 = new function () {  
                      this.init = function () {alert(wert);};  
                    };  
                  };
                  

                  Wenn ich jetzt lib.func1.init(); aufrufe, ist "wert" natürlich nicht definiert und würde einen Fehler ergeben. Ich möchte nun aber "wert" im Bereich von func1 vorbelegen und ggf. vor dem Aufruf überschreiben. Etwa in der Art lib.func1.wert = 'bla'; Eine Übergabe beim Aufruf kommt nicht in Frage.

                  Wie löse ich das? Meine Versuche hatten bisher leider keinen Erfolg.

                  Ralf

                  1. Nun habe ich doch noch eine konkrete Frage. Nehmen wir mal folgende Konstruktion an:

                    var lib = new function () {
                      this.func1 = new function () {
                        this.init = function () {alert(wert);};
                      };
                    };

                    
                    >   
                    > Wenn ich jetzt lib.func1.init(); aufrufe, ist "wert" natürlich nicht definiert und würde einen Fehler ergeben. Ich möchte nun aber "wert" im Bereich von func1 vorbelegen und ggf. vor dem Aufruf überschreiben.  
                      
                    Kommt darauf an, als was du dieses Attribut siehst.  
                    Ist es eine Eigenschaft von lib?  
                    Dann so:  
                    ~~~javascript
                    var lib = new function () {  
                      this.wert = 0;  
                      var self = this;  
                      this.func1 = new function () {  
                      this.init = function () {alert(self.wert);};  
                      };  
                    };  
                    lib.wert = 1;  
                    lib.func1.init();  
                    
                    

                    Ist es eine Eigenschaft von lib.func1, dann so:

                    var lib = new function () {  
                      this.func1 = new function () {  
                     this.wert = 0;  
                        this.init = function () {alert(this.wert);};  
                      };  
                    };  
                    lib.func1.wert = 1;  
                    lib.func1.init();  
                    
                    

                    Struppi.

                    1. Kommt darauf an, als was du dieses Attribut siehst.
                      Ist es eine Eigenschaft von lib?
                      Dann so:

                      var lib = new function () {

                      this.wert = 0;
                        var self = this;
                        this.func1 = new function () {
                        this.init = function () {alert(self.wert);};
                        };
                      };
                      lib.wert = 1;
                      lib.func1.init();

                      
                      >   
                      > Ist es eine Eigenschaft von lib.func1, dann so:  
                      > ~~~javascript
                      
                      var lib = new function () {  
                      
                      >   this.func1 = new function () {  
                      >  this.wert = 0;  
                      >     this.init = function () {alert(this.wert);};  
                      >   };  
                      > };  
                      > lib.func1.wert = 1;  
                      > lib.func1.init();  
                      > 
                      
                      

                      Letzteres wollte ich in diesem Fall. Ich hätte nicht erwartet, dass this.wert geht, weil ich davon ausgegangen wäre, dass this sich in diesem Fall nur lokal auf die init Funktion bezieht.
                      Aber es handelt sich ja in diesem Fall um keine Instanz der Funktion - richtig?

                      Das erste Beispiel wird mir aber sicherlich auch noch nützlich sein.

                      Vielen Dank für die Hilfen!

                      Ralf

                      1. Kommt darauf an, als was du dieses Attribut siehst.
                        Ist es eine Eigenschaft von lib?
                        Dann so:

                        var lib = new function () {

                        this.wert = 0;
                          var self = this;
                          this.func1 = new function () {
                          this.init = function () {alert(self.wert);};
                          };
                        };
                        lib.wert = 1;
                        lib.func1.init();

                        
                        > >   
                        > > Ist es eine Eigenschaft von lib.func1, dann so:  
                        > > ~~~javascript
                        
                        var lib = new function () {  
                        
                        > >   this.func1 = new function () {  
                        > >  this.wert = 0;  
                        > >     this.init = function () {alert(this.wert);};  
                        > >   };  
                        > > };  
                        > > lib.func1.wert = 1;  
                        > > lib.func1.init();  
                        > > 
                        
                        

                        Letzteres wollte ich in diesem Fall. Ich hätte nicht erwartet, dass this.wert geht, weil ich davon ausgegangen wäre, dass this sich in diesem Fall nur lokal auf die init Funktion bezieht.
                        Aber es handelt sich ja in diesem Fall um keine Instanz der Funktion - richtig?

                        Kommt darauf an was du mit der Funktion meinst.
                        this.func1 = new function () {
                            this.wert = 0;
                            this.init = function () {alert(this.wert);};
                           };

                        this.func1 ist eine Instanz der anonymen Funktion, inerhalb der Funktion ist this die Instanz. Du kannst dort auch lokale Variabeln definieren.

                        Ein Beispiel das mher OO ist:

                        this.func1 = new function () {  
                            var wert = 0;  
                            this.init = function () {alert(wert);};  
                            this.setWert = function(w) { wert = w; };  
                            this.getWert = function() { return wert; };  
                        };  
                        
                        

                        Das erste Beispiel wird mir aber sicherlich auch noch nützlich sein.

                        Nach deinen bisherigen Beispielen zu urteilen, denk ich mir das auch ;-)

                        Struppi.

      2. Hallo,

        Dann müsste ich aber auch jegliche Werte, die ich jetzt in Unterobjekten definiert habe, in Funktionen kapseln?

        Die Frage verstehe ich nicht.
        Wahrscheinlich lautet die Antwort nein.
        Dass Struppi Funktionen genommen hat, war nur ein Beispiel, du kannst auch Objects nehmen und es funktioniert genauso.

        this.module1 = { version : ..., init : ..., ... };
        this.module2 = { version : ..., init : ..., ... };

        Und vordefinierte Werte zu überschreiben, wäre wohl dann etwas komplizierter, wenn ich das richtig erfasst habe.

        Was meinst du damit? Welche vordefinierten Werte?

        Nach meinem Posting bin ich auf die Idee gekommen, die Definition doch außerhalb vorzunehmen:
        lib.modules = [lib.func1, lib.func2];

        Ich glaube, du denkst gerade superkompliziert, viel zu kompliziert für JavaScript.

        Was hast du mit diesem Array vor?
        Wann greifst du z.B. auf modules[0] zu, wo du nicht modules.module1 verwenden kannst oder willst? (Warum?)

        Das mit der Sichtbarkeit verstehe ich auch nicht ganz.

        Wahrscheinlich willst du so etwas wie »interne« Module, die aber von außen nicht sichtbar sind. Klar, das kannst du mit der Unterscheidung von öffentlichen (this.member) und privaten Eigenschaften (var lokaleVariable) im Konstruktor lösen. Man müsste nur eine einheitliche Ansprechweise von Modul zu Modul finden.

        var lib = new function() {  
           /* Public Modules */  
           this.module1 = {  
              init : function () {  
                 internalModule.init();  
              }  
           };  
          
           /* Private Modules */  
           var internalModule = {  
              init : function () {  
                 lib.module1.doSomething();  
              }  
           };  
          
           /* Public Modules Hash */  
           this.modules = {}; // oder ein Array, wie du willst  
           for (var f in this)  
              if (this.hasOwnProperty(f))  
                 this.modules[f] = this[f];  
          
           return lib;  
        };  
          
        lib.module1.init();  
          
        for (var id in lib.modules) {  
           alert(id);  
        }
        

        Diese public/private-Unterscheidung kann man natürlich auch anders lösen.

        Mathias

        1. Hallo,

          this.modules = {}; // oder ein Array, wie du willst
             for (var f in this)
                if (this.hasOwnProperty(f))
                   this.modules[f] = this[f];

          Das dürfte rekursiv werden, wenn schon, dann anders herum:

          var modules = {};
          .. Schleife ...
          this.modules = modules;
          (Evtl. delete modules;)

          return lib;

          Vergiss die Zeile.

        2. In meiner Antwort auf Struppis Beitrag habe ich schon einiges dazu geschrieben, was ich erreichen will und möchte daher hier nur auf Details eingehen.

          Dass Struppi Funktionen genommen hat, war nur ein Beispiel, du kannst auch Objects nehmen und es funktioniert genauso.

          this.module1 = { version : ..., init : ..., ... };
          this.module2 = { version : ..., init : ..., ... };

          Das habe ich verstanden.

          Und vordefinierte Werte zu überschreiben, wäre wohl dann etwas komplizierter, wenn ich das richtig erfasst habe.

          Was meinst du damit? Welche vordefinierten Werte?

          Die Funktionen, welche von außen (öffentlich?) benutzbar sein sollen, können vordefinierte Werte haben. Es soll aber auch möglich sein, diese Werte möglichst einfach vor dem Aufruf der Funktion zu überschreiben. Natürlich könnte man diese Werte im Funktionsaufruf selbst übergeben, aber dadurch wird der Aufruf und die Definition für Laien komplizierter. Einfache Zuweisungen versteht so ziemlich jeder und ein Funktionsaufruf ist auch noch einfach, aber wenn  im Aufruf Objektstrukturen übergeben werden, kann das leicht zu Fehlern führen.

          Nach meinem Posting bin ich auf die Idee gekommen, die Definition doch außerhalb vorzunehmen:
          lib.modules = [lib.func1, lib.func2];

          Ich glaube, du denkst gerade superkompliziert, viel zu kompliziert für JavaScript.

          Das befürchte ich auch ...

          Was hast du mit diesem Array vor?
          Wann greifst du z.B. auf modules[0] zu, wo du nicht modules.module1 verwenden kannst oder willst? (Warum?)

          Ich greife dann über den Array zu, wenn ich gar nicht weiß, welche Module es gibt. In diesem Beispiel würde ich Eigenschaften (z.B. die Versionsbezeichnung) über alle enthaltenen Elemente ausgeben. Welche das sind, sollte intern definiert werden.

          Ralf

          1. Die Funktionen, welche von außen (öffentlich?) benutzbar sein sollen, können vordefinierte Werte haben. Es soll aber auch möglich sein, diese Werte möglichst einfach vor dem Aufruf der Funktion zu überschreiben. Natürlich könnte man diese Werte im Funktionsaufruf selbst übergeben, aber dadurch wird der Aufruf und die Definition für Laien komplizierter. Einfache Zuweisungen versteht so ziemlich jeder und ein Funktionsaufruf ist auch noch einfach, aber wenn  im Aufruf Objektstrukturen übergeben werden, kann das leicht zu Fehlern führen.

            Das will mir nicht einleuchten.

            lib.funktion( wert );

            ist für den Laien sicher einfacher als:

            lib.eigenschaft.parameter = wert;

            zumal du dir so die Möglichkeit der Prüfung verbaust und eine weitere Quelle für Tippfehler einbaust.

            Wenn die Parameter Struktur zu komliziert würde, lassen sich natürlich auch entpsrechende Funktionen einbauen:

            lib.setValue(wert);

            was auch mehr im Sinn der OO Programmierung wäre.

            Ich greife dann über den Array zu, wenn ich gar nicht weiß, welche Module es gibt. In diesem Beispiel würde ich Eigenschaften (z.B. die Versionsbezeichnung) über alle enthaltenen Elemente ausgeben. Welche das sind, sollte intern definiert werden.

            du kannst ja jederzeit überprüfen ob ein "Modul" (also eine Eigenschaft existiert) ein Array ist dazu nicht nötig, im gegenteil es macht sogar nur mehr Arbeit und ist langsam.

            Wenn du z.b. eine Funktion nur aufrufen willst, wenn diese vorhanden ist:
            if(typeof lib.func1 == 'function') lib.func();

            Struppi.

            1. Das will mir nicht einleuchten.

              lib.funktion( wert );

              ist für den Laien sicher einfacher als:

              lib.eigenschaft.parameter = wert;

              Wenn die Zeile bis auf "wert" vorgegeben ist, sieht das schon etwas anders aus, aber vielleicht ist es auch nur eine Betrachtungsweise.

              zumal du dir so die Möglichkeit der Prüfung verbaust und eine weitere Quelle für Tippfehler einbaust.

              Damit hast du sicherlich Recht.

              Wenn die Parameter Struktur zu komliziert würde, lassen sich natürlich auch entpsrechende Funktionen einbauen:

              lib.setValue(wert);

              was auch mehr im Sinn der OO Programmierung wäre.

              Akzeptiert!

              Ich greife dann über den Array zu, wenn ich gar nicht weiß, welche Module es gibt. In diesem Beispiel würde ich Eigenschaften (z.B. die Versionsbezeichnung) über alle enthaltenen Elemente ausgeben. Welche das sind, sollte intern definiert werden.

              du kannst ja jederzeit überprüfen ob ein "Modul" (also eine Eigenschaft existiert) ein Array ist dazu nicht nötig, im gegenteil es macht sogar nur mehr Arbeit und ist langsam.

              Wenn du z.b. eine Funktion nur aufrufen willst, wenn diese vorhanden ist:
              if(typeof lib.func1 == 'function') lib.func();

              Mein Beispiel war vermutlich schlecht gewählt, denn es sollte nur einen Nebenaspekt beleuchten, der für die eigentliche Funktionalität unwichtig ist. Ich wollte eine Möglichkeit haben, für diverse Funktionen deren Versionsbezeichnung auszugeben. Und zwar genau für die Funktionen, die in der Bibliothek eine Versionskennung besitzen. Das weiß ich von außen nicht. Also meinte ich ein Objekt zu benötigen, in dem alle diese Funktionen definiert sind.
              Der bessere Ansatz wäre wohl eine Funktion, welche alle Funktionsobjekte auf das Vorhandensein einer Versionskennung untersucht und diese dann ausgibt.

              Vielen Dank für die Anregungen. So wie ich es jetzt sehe, war mein Ansatz falsch und ich werde es jetzt mit einem Funktionsobjekt als Ausgangsbasis versuchen.

              Ralf