pcworld: Problem bei Wertzuweisung auf Objekt

Ich bin grad am verzweifeln und weiß nicht, was ich falsch mache. Irgendwie blick ich die Welt nicht mehr... wird ein kleiner Fehler sein, den ich vor lauter Coden nicht mehr sehen will/kann... :-/
Ich habe folgenden Code (vereinfacht), der eine Baumstruktur darstellen soll - nutzt prototype.js v1.7:

var NodeArray = Class.create({  
	initialize: function() {  
		this.addNode = this.addNode.bind(this);  
		  
		this.selectedNode = null;  
	},  
  
	addNode: function(node) {  
		node.nodearr = this;  
	}  
});  
  
var Node = Class.create({  
	initialize: function(parent, no) {  
		this.addNode = this.addNode.bind(this);  
		this.printout = this.printout.bind(this);  
		this.no = no;  
		  
		this.parent = parent;  
		this.children = new Array();  
		this.nodearr = null;  
		  
		this.printout.delay(0.5); // Delay, damit genug Zeit ist, um den Wert per #addNode zu setzen  
	},  
  
	addNode: function(node) {  
		alert("Node#addNode: " + this.no + ": " + this.nodearr);  
		node.nodearr = this.nodearr;  
		this.children.push(node);  
	},  
	  
	printout: function() {  
		alert("Node#printout: " + this.no + ": " + this.nodearr);  
	}  
	  
});  
  
var arr = new NodeArray();  
var n = new Node(arr, 1); // Knoten für die zweite Schicht erzeugen...  
n.addNode(new Node(n, 2)); // ...und hinzufügen  
arr.addNode(new Node(arr, 3));  
arr.addNode(n);  
arr.addNode(new Node(arr, 4));

Bei "Kindknoten" der Baumstruktur ist this.nodearr nicht bekannt, bzw. es ist der Wert, der in der initialize-Funktion der Klasse Node gesetzt wurde (null), wobei dieser Wert eigentlich in der addNode-Funktion der Klasse Node gesetzt werden sollte. Nur warum das nicht geschieht, ist mir ein großes Fragezeichen.
Eigentlich sollen die Nodes der obersten Schicht einfach nur ihr Wissen der Instanz der Klasse NodeArray an ihre Kindknoten weitergeben, nur das will nicht klappen, obwohl diese eine Instanz haben, was die Ausgabe der alert()-Aufrufe deutlich macht:

Node#addNode: 1: null // hier ist this.nodearr unbekannt...
Node#printout: 1: [object Object] // ...während es in der printout-Funktion bekannt ist!
Node#printout: 2: null // ist als einziger ein Knoten der zweiten Schicht
Node#printout: 3: [object Object]
Node#printout: 4: [object Object]

Ich such wirklich schon ewig nach dem Fehler, nur finde ich ihn nicht.

Ich würde mich freuen, wenn jemand meinen Code besser verstehen könnte als ich und mir helfen könnte! :)

  1. Ich habe folgenden Code (vereinfacht), der eine Baumstruktur darstellen soll

    Bevor ich mich intensiver mit dem Code beschäftige, warum fügst du die Liste dem Knoten zu? Normal wär, andersherum.

    Struppi.

    1. Bevor ich mich intensiver mit dem Code beschäftige, warum fügst du die Liste dem Knoten zu? Normal wär, andersherum.

      Die Knoten (vom Typ Node) der ersten Schicht des Baums werden einer Liste (vom Typ NodeArray) hinzugefügt. Alle ihnen untergeordnete Knoten werden dem jeweiligen Knoten hinzugefügt.
      Nun soll jeder beliebige Knoten im Baum die Instanz vom NodeArray (die Liste, die die Knoten der ersten Schicht enthält) kennen, da diese "besondere" Funktionen bereitstellt, z. B. ist im NodeArray abgespeichert, welcher Knoten gerade ausgewählt ist (nicht im obigen Quelltext enthalten).

      1. Bevor ich mich intensiver mit dem Code beschäftige, warum fügst du die Liste dem Knoten zu? Normal wär, andersherum.
        Die Knoten (vom Typ Node) der ersten Schicht des Baums werden einer Liste (vom Typ NodeArray) hinzugefügt. Alle ihnen untergeordnete Knoten werden dem jeweiligen Knoten hinzugefügt.

        Das beantwortet nicht meine Frage, bzw. im gegenteil - warum fügst du nicht die Knoten der Liste zu?

        Nun soll jeder beliebige Knoten im Baum die Instanz vom NodeArray (die Liste, die die Knoten der ersten Schicht enthält) kennen, da diese "besondere" Funktionen bereitstellt, z. B. ist im NodeArray abgespeichert, welcher Knoten gerade ausgewählt ist (nicht im obigen Quelltext enthalten).

        Auch das ist keine Grund, warum du die Liste dem Knoten hinzufügst.

        Ausserdem frage ich mich was diese Zeilen sollen:
        this.addNode = this.addNode.bind(this);
        Die Methode addNode ist doch bereits an this gebunden, was soll hier passieren?

        Struppi.

        1. Das beantwortet nicht meine Frage, bzw. im gegenteil - warum fügst du nicht die Knoten der Liste zu?

          Das werden sie doch, siehe die paar letzten Zeilen im Script. Die Knoten (der ersten Schicht) werden dem NodeArray hinzugefügt.

          *Zusätzlich* soll jeder Knoten eine Instanz der ArrayList besitzen.
          Der Grund ist, dass bei einem Klick auf bestimmte Elemente im DOM (der Baum wird graphisch dargestellt, wie z. B. der Baum im klassischen Windows Explorer) eine Funktion des entsprechenden Node-Objekts aufgerufen wird (Eventhandler), das wiederum den gerade selektierten Knoten im NodeArray ändert.

          Ausserdem frage ich mich was diese Zeilen sollen:
          this.addNode = this.addNode.bind(this);
          Die Methode addNode ist doch bereits an this gebunden, was soll hier passieren?

          Ich dachte, das müsste man explizit machen, damit ich in einer Instanz in jeder Funktion das gleiche this-Objekt habe.
          Oder erledigt die Prototype-Lib das für mich bei der Verwendung von Class#create?

          1. Das beantwortet nicht meine Frage, bzw. im gegenteil - warum fügst du nicht die Knoten der Liste zu?
            Das werden sie doch, siehe die paar letzten Zeilen im Script. Die Knoten (der ersten Schicht) werden dem NodeArray hinzugefügt.

            *Zusätzlich* soll jeder Knoten eine Instanz der ArrayList besitzen.

            Ja, und genau das machst du andersherum sinnvoller, als wie du es jetzt versuchst.

            Der Grund ist, dass bei einem Klick auf bestimmte Elemente im DOM (der Baum wird graphisch dargestellt, wie z. B. der Baum im klassischen Windows Explorer) eine Funktion des entsprechenden Node-Objekts aufgerufen wird (Eventhandler), das wiederum den gerade selektierten Knoten im NodeArray ändert.

            Das klingt nochmals veriwrrend, was ist der Unterschied zwischen dem "entsprechenden Node-Objekt" und dem "selektierten Knoten", das klingt für mich nach dem gleichen Objekt.

            Ich dachte, das müsste man explizit machen, damit ich in einer Instanz in jeder Funktion das gleiche this-Objekt habe.

            Du hast doch die Funktkion bereits an das Objekt gebunden, durch den Aufruf von Class.

            Oder erledigt die Prototype-Lib das für mich bei der Verwendung von Class#create?

            Genau.

            Struppi.

            1. Du hast doch die Funktkion bereits an das Objekt gebunden, durch den Aufruf von Class.

              Oder erledigt die Prototype-Lib das für mich bei der Verwendung von Class#create?

              Genau.

              Das ist nicht der Fall, siehe Antwort von molily.

              1. Du hast doch die Funktkion bereits an das Objekt gebunden, durch den Aufruf von Class.

                Oder erledigt die Prototype-Lib das für mich bei der Verwendung von Class#create?

                Genau.
                Das ist nicht der Fall, siehe Antwort von molily.

                Doch ist es, wo steht da was anderes?

                Dort wo du bind aufrufst, ist es nicht nötig, ich weiß nicht wie oft ich das wiederholen muss. Es macht nichts weiter als eine zusätzliche Funktionsschicht zu bauen, die aber hier nicht nötig ist.

                Struppi.

                1. Doch ist es, wo steht da was anderes?

                  Dort wo du bind aufrufst, ist es nicht nötig, ich weiß nicht wie oft ich das wiederholen muss. Es macht nichts weiter als eine zusätzliche Funktionsschicht zu bauen, die aber hier nicht nötig ist.

                  Ich habe auch schon mehrmals erwähnt, dass der eigentliche Code (welcher nicht in diesem Thread ist) umfangreicher ist, und bind() an diesen Stellen nötig ist. Class#create macht das nicht für mich, siehe: https://forum.selfhtml.org/?t=203342&m=1374916

        2. Ausserdem frage ich mich was diese Zeilen sollen:
          this.addNode = this.addNode.bind(this);
          Die Methode addNode ist doch bereits an this gebunden, was soll hier passieren?

          Hab es mal ohne die *.bind(this)-Aufrufe ausprobiert, da kommt nur noch mehr Verwirrung ins Spiel. Da ist dann bei allen alert-Aufrufen nicht mal mehr this.no bekannt.

          1. Das Binding brauchst du (nur), wenn du eine Methode anders this.methode() aufrufst, sondern beispielsweise verzögert mit setTimeout/setInterval oder als Event-Handler. Du verwendest ja this.printout.delay(0.5), was auf setTimeout hinausläuft. in solchen Fällen musst du die betreffenden Methoden an das Instanzobjekt binden, damit this auf das Instanzobjekt verweist.

            Mathias

            1. Das Binding brauchst du (nur), wenn du eine Methode anders this.methode() aufrufst, sondern beispielsweise verzögert mit setTimeout/setInterval oder als Event-Handler. Du verwendest ja this.printout.delay(0.5), was auf setTimeout hinausläuft. in solchen Fällen musst du die betreffenden Methoden an das Instanzobjekt binden, damit this auf das Instanzobjekt verweist.

              Funktionen (bzw. darf man hier in dieser Fake-OOP Methode sagen?) werden ja auch oft außerhalb der eigenen Instanz/Klasse aufgerufen.
              Wenn ich im "Konstruktor" (#initialize) einfach immer alle Funktionen an this "binde", sollten mir ja eigentlich keine Nachteile entstehen - oder sehe ich das falsch?

              1. Wenn ich im "Konstruktor" (#initialize) einfach immer alle Funktionen an this "binde", sollten mir ja eigentlich keine Nachteile entstehen - oder sehe ich das falsch?

                Jein. Es schadet natürlich nicht, bind() vorsorglich auf alle Methoden anzuwenden, selbst wenn sie ohnehin immer über this.methode() aufgerufen werden. Allerdings kapselt es halt die tatsächliche Methoden in einer Closure. Das braucht halt zusätzliche Zeit und Speicher. Beim Aufrufen der Methode wird die Closure und die tatsächliche Methode aufgerufen, d.h. die Anzahl der Funktionsaufrufe verdoppelt sich. Das kann zu Performance-Problemen führen, siehe Performance von JavaScript-Closures (am Ende diskutiere ich auch Binding).

                Ob dieser Overhead ins Gewicht fällt, hängt vom Einzelfall ab. Function.prototype.bind ist wie gesagt seit ECMAScript 5 Teil des JavaScript-Kerns und damit sind die nativen Browser-Implementierungen auch weitaus schneller geworden.

                Mathias

                1. Jein. Es schadet natürlich nicht, bind() vorsorglich auf alle Methoden anzuwenden, selbst wenn sie ohnehin immer über this.methode() aufgerufen werden. Allerdings kapselt es halt die tatsächliche Methoden in einer Closure. Das braucht halt zusätzliche Zeit und Speicher. Beim Aufrufen der Methode wird die Closure und die tatsächliche Methode aufgerufen, d.h. die Anzahl der Funktionsaufrufe verdoppelt sich. Das kann zu Performance-Problemen führen, siehe Performance von JavaScript-Closures (am Ende diskutiere ich auch Binding).

                  Ob dieser Overhead ins Gewicht fällt, hängt vom Einzelfall ab.

                  Danke für den Hinweis. Nach den Zahlen aus deinem Artikel dürfte ja sowas nur bei sehr oft sich wiederholenden Schleifen etwas ausmachen. Ich werde in Zukunft trotzdem einfach mal darauf achten.

                  Function.prototype.bind ist wie gesagt seit ECMAScript 5 Teil des JavaScript-Kerns und damit sind die nativen Browser-Implementierungen auch weitaus schneller geworden.

                  Ich war mir unsicher, ob Function#bind Standard ist - aber nun ist das auch klar.

        3. Ausserdem frage ich mich was diese Zeilen sollen:
          this.addNode = this.addNode.bind(this);
          Die Methode addNode ist doch bereits an this gebunden, was soll hier passieren?

          Function.prototype.bind wrappt ein Funktionsobjekt und sorgt dafür, dass sie immer im Kontext des angegebenen Objektes ausgeführt wird. Das ist nicht standardmäßig der Fall, siehe http://molily.de/js/organisation-verfuegbarkeit.html.

          Mathias

          1. Function.prototype.bind wrappt ein Funktionsobjekt und sorgt dafür, dass sie immer im Kontext des angegebenen Objektes ausgeführt wird. Das ist nicht standardmäßig der Fall, siehe http://molily.de/js/organisation-verfuegbarkeit.html.

            Wow, deine Seite ist echt informativ! Mir ist nun einiges klarer, vor allem was this anbelangt und wie die bind-Funktion eigentlich funktioniert.

          2. Ausserdem frage ich mich was diese Zeilen sollen:
            this.addNode = this.addNode.bind(this);
            Die Methode addNode ist doch bereits an this gebunden, was soll hier passieren?

            Function.prototype.bind wrappt ein Funktionsobjekt und sorgt dafür, dass sie immer im Kontext des angegebenen Objektes ausgeführt wird. Das ist nicht standardmäßig der Fall,

            Hier aber schon. Er verwendet prototype und erzeugt eine Class-Objekt mit dieser Methode. Dieser Aufruf im Konstruktor ist unnötig und macht den Code noch verwirrender als er schon ist.

            Struppi.

            1. Function.prototype.bind wrappt ein Funktionsobjekt und sorgt dafür, dass sie immer im Kontext des angegebenen Objektes ausgeführt wird. Das ist nicht standardmäßig der Fall,

              Hier aber schon. Er verwendet prototype und erzeugt eine Class-Objekt mit dieser Methode.

              Was ich meinte, war:

              <!DOCTYPE html>  
              <html><head>  
              <script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>  
              <script>  
              [code lang=javascript]var PseudoClass = Class.create({  
                 className : "PseudoClass",  
                 method : function () {  
                    alert(this + '\n' + typeof this + '\n' + (this instanceof PseudoClass) + '\n' + this.className);  
                 }  
              });  
              var instance = new PseudoClass;  
              instance.method.delay(1); // Geht nicht  
              instance.method.bind(instance).delay(1); // Geht
              

              </script>
              </head><body></body></html>[/code]

              Prototype bindet die Methode hier nicht standardmäßig an die Instanz. instance.method ist exakt das Funktionsobjekt, was man Class.create im Object-Literal übergeben hat. Es wird nicht automatisch in eine Closure gewrappt.
              Vielmehr bindet delay this an die Methode selbst.

              Mathias

              1. Hier aber schon. Er verwendet prototype und erzeugt eine Class-Objekt mit dieser Methode.

                Was ich meinte, war:

                Mir ist schon klar wofür bind() verwendet wird, aber ich bezog mich auf den Quelltext des OP. Da ist das bind an dieser Stelle unnötig und erzeugt lediglich eine zusätzliche Schicht und ich fragte mich welchen Zweck er damit verfolgt. Die Erklärung war nicht erleuchtend, denn das bind hat an dieser Stelle keinerlei Auswirkung auf die Funktionalität nur negative auf die Geschwindigkeit.

                Struppi.

                1. Mir ist schon klar wofür bind() verwendet wird, aber ich bezog mich auf den Quelltext des OP. Da ist das bind an dieser Stelle unnötig und erzeugt lediglich eine zusätzliche Schicht und ich fragte mich welchen Zweck er damit verfolgt. Die Erklärung war nicht erleuchtend, denn das bind hat an dieser Stelle keinerlei Auswirkung auf die Funktionalität nur negative auf die Geschwindigkeit.

                  Der Quelltext in meinem ersten Post war nur dazu gedacht, das Problem zu verdeutlichen. Der eigentliche Code in meiner Anwendung hat um einiges mehr Funktionalität und die Bindings sind teilweise nötig.
                  Und ich programmiere wirklich nicht auf Nanosekunden.

                  1. Mir ist schon klar wofür bind() verwendet wird, aber ich bezog mich auf den Quelltext des OP. Da ist das bind an dieser Stelle unnötig und erzeugt lediglich eine zusätzliche Schicht und ich fragte mich welchen Zweck er damit verfolgt. Die Erklärung war nicht erleuchtend, denn das bind hat an dieser Stelle keinerlei Auswirkung auf die Funktionalität nur negative auf die Geschwindigkeit.
                    Der Quelltext in meinem ersten Post war nur dazu gedacht, das Problem zu verdeutlichen. Der eigentliche Code in meiner Anwendung hat um einiges mehr Funktionalität und die Bindings sind teilweise nötig.

                    Das ist nur dort nötig, wo ein Kontextwechsel stattfindet. Also bei z.b. Timer oder Eventfunktionen. Du tust es aber in deinem Beispiel innerhalb des Objektes, dort ist kein bind() nötig.

                    Und ich programmiere wirklich nicht auf Nanosekunden.

                    Da du hier von nodes sprichst, vermute ich mal du arbeitest mit dem DOM. Daneben kommen Schleifen bzw. Arrays zum Einsatz, also u.U. viele Nodes.
                    Im firefox läuft die Variante mit bind 8x langsamer als ohne, d.h. du bist insgesamt betrachtet schnell im Zehntelsekundenbereich.

                    Und ncoh mal, weil das offensichtlich nicht klar ist, mir geht es nicht um bind() an sich, sondern um dieses Konstrukt:

                           initialize: function(parent, no) {  
                                    this.addNode = this.addNode.bind(this);  
                                    this.printout = this.printout.bind(this);  
                    
                    

                    Das einzige was hier passiert, ist das die Funktion um den Faktor 8 gebremst wird ohne irgendeinen Nutzen davon zu haben.

                    Struppi.

                    1. Da du hier von nodes sprichst, vermute ich mal du arbeitest mit dem DOM. Daneben kommen Schleifen bzw. Arrays zum Einsatz, also u.U. viele Nodes.
                      Im firefox läuft die Variante mit bind 8x langsamer als ohne, d.h. du bist insgesamt betrachtet schnell im Zehntelsekundenbereich.

                      Das Endprodukt wird ausschließlich in einer Webkit-Engine mit der V8 JavaScript Engine laufen.
                      Ich kann mir kaum vorstellen, dass das viel Zeit sein wird, die ich da einbüße - und selbst wenn, dann wartet der User halt mal eine halbe Sekunde länger. Und da die Daten, die im Baum dargestellt werden, sowieso dynamisch von einem Server geholt werden, spielt hier die Anfrage die größte Zeit.

                      Und ncoh mal, weil das offensichtlich nicht klar ist, mir geht es nicht um bind() an sich, sondern um dieses Konstrukt:

                      initialize: function(parent, no) {

                      this.addNode = this.addNode.bind(this);
                                      this.printout = this.printout.bind(this);

                      
                      > Das einzige was hier passiert, ist das die Funktion um den Faktor 8 gebremst wird ohne irgendeinen Nutzen davon zu haben.  
                      
                      Das habe ich gemacht, um mir an anderen Stellen im Code das ständige \*.bind(this) zu ersparen. Aber ich seh es ja ein, dass das evtl. die Perfomance beeinflussen könnte.  
                      Allerdings wird bei jedem Aufruf von \*.bind(this) ja auch eine neue Funktion erstellt und die wird zurückgegeben. Wenn ich gleich im Konstruktor alle Funktionen an this "binde", müssen weniger oft neue Funktionen erstellt werden. Vielleicht ist auch das ein Geschwindigkeitsvorteil?!  
                      Wobei es mir auch auf ein paar Millisekunden nicht ankommt.
                      
                      1. Das einzige was hier passiert, ist das die Funktion um den Faktor 8 gebremst wird ohne irgendeinen Nutzen davon zu haben.
                        Das habe ich gemacht, um mir an anderen Stellen im Code das ständige *.bind(this) zu ersparen. Aber ich seh es ja ein, dass das evtl. die Perfomance beeinflussen könnte.

                        Du ersparst dir nichts dadurch, im gegenteil du machst dir damit evtl. sogar Probleme. Hier in dem Fall ist es nicht weiter tragisch, aber wenn du einen Fehler machst, dann kann es passieren, dass die Methode nicht mehr in dem Kontext aufgerufen wird, in dem sie aufgerufen werden soll.

                        Allerdings wird bei jedem Aufruf von *.bind(this) ja auch eine neue Funktion erstellt und die wird zurückgegeben. Wenn ich gleich im Konstruktor alle Funktionen an this "binde", müssen weniger oft neue Funktionen erstellt werden. Vielleicht ist auch das ein Geschwindigkeitsvorteil?!

                        Nein. Die Funktionen werden sowieso automatisch an this gebunden, dass macht in dem Fall Class.create für dich, du legst einfach nur noch eine Funktionsschicht drumherum.

                        In etwa so:

                        function F() {  
                        this.funktion = function () {};  
                          
                        this.funktion = ( function(kontext, f) {  
                            return function () {  
                                f.call(kontext);  
                             };  
                        )(this, this.funktion);  
                          
                        }
                        

                        Das ist eine aufwändige Prozedure für nichts.

                        Wobei es mir auch auf ein paar Millisekunden nicht ankommt.

                        Ja gut, es gibt auch Leute die fahren mit angezogener Handbremse. Es macht aber keinen Sinn.

                        Struppi.

                        1. Allerdings wird bei jedem Aufruf von *.bind(this) ja auch eine neue Funktion erstellt und die wird zurückgegeben. Wenn ich gleich im Konstruktor alle Funktionen an this "binde", müssen weniger oft neue Funktionen erstellt werden. Vielleicht ist auch das ein Geschwindigkeitsvorteil?!

                          Das ist erstmal richtig. Deshalb setzen manche JavaScript-Implementierungen von Function.prototype.bind einen Memoizer ein (habe ich in meinem Artikel auch erwähnt). D.h. die erzeugten Closures werden gecachet und wiederverwendet. Wenn ich die bind-Methode ein und derselben Funktion mehfach mit demselben this-Wert aufrufe, dann bekomme ich keine neue Wrapper-Funktion, sondern dieselbe. Beispiel:

                          var f1 = f.bind(this);
                          // ...
                          var f2 = f.bind(this);

                          f1 wäre in dem Fall identisch mit f2. Ich denke mal, dass die nativen Implementierungen (V8 implementiert bind nativ) das intern so oder ähnlich optimieren. f1 ist dann aber nicht identisch mit f2, nach außen hin sind das schon zwei Objekte.

                          Die Performance davon habe ich aber noch nicht gemessen. Ich habe im Praxiseinsatz aber auch nur einmal erlebt, dass sich solches Wrapping merklich negativ auswirkt. Da ging es um eine Canvas-basierte Anwendung, die mit 60 Frames die Sekunde tausende Objekte rendern sollte. Ich hatte zuerst eine OOP-Bibliothek eingesetzt, welche sämtliche Methoden gewrappt hat. Dieser Overhead war deutlich spürbar. Letztlich habe ich alle externen Bibliotheken entfernt und es mit einfachen Pseudoklassen implementiert. Die wenigen Stellen, wo Binding nötig war, habe ich mit gezielten Closures gelöst. (Damals hat aber auch noch kein Browser bind nativ implementiert.)

                          Nein. Die Funktionen werden sowieso automatisch an this gebunden, dass macht in dem Fall Class.create für dich, du legst einfach nur noch eine Funktionsschicht drumherum.

                          Ich glaube wir benutzen den Begriff anders und daher kommt es zu Missverständnissen, ohne dass wir uns in der Sache widersprechen.

                          Wenn ich von Binding/Binden rede, dann meinte ich, dass sichergestellt wird, dass this in einer Funktion immer auf die Instanz zeigt - eben das, was Function.prototype.bind macht. Ein solches festes Binding erzeugt PrototypeJS nicht automatisch. (Wolltest du auch nicht behaupten, soweit ich dich jetzt verstanden habe.)

                          Ich glaube, du meintest mit Binding, dass PrototypeJS die Methoden aus dem übergebenen Object-Literal an den Prototype kopiert und sie daher an der Instanz zur Verfügung stehen. Klar, das bekommt man frei Haus.

                          Mathias

                          1. Die Performance davon habe ich aber noch nicht gemessen. Ich habe im Praxiseinsatz aber auch nur einmal erlebt, dass sich solches Wrapping merklich negativ auswirkt. Da ging es um eine Canvas-basierte Anwendung, die mit 60 Frames die Sekunde tausende Objekte rendern sollte. Ich hatte zuerst eine OOP-Bibliothek eingesetzt, welche sämtliche Methoden gewrappt hat. Dieser Overhead war deutlich spürbar. Letztlich habe ich alle externen Bibliotheken entfernt und es mit einfachen Pseudoklassen implementiert. Die wenigen Stellen, wo Binding nötig war, habe ich mit gezielten Closures gelöst. (Damals hat aber auch noch kein Browser bind nativ implementiert.)

                            Solchen Rechenaufwand werden meine Scripte wohl nicht aufbringen.
                            Wie gesagt, am meisten Zeit brauchen sowieso die Netzwerk-Abfragen, da wird ein Binding hin oder her auch keinen Unterschied mehr machen aus der Perspektive des Users.

                2. Da ist das bind an dieser Stelle unnötig und erzeugt lediglich eine zusätzliche Schicht und ich fragte mich welchen Zweck er damit verfolgt.

                  Bei der von dir genannten addNode-Methode ist es unnötig (ausgehend von dem geposteten Quelltext).

                  Bei der printout-Methode ist es jedoch nötig, denn pcworld ruft diese mit delay() auf und erwartet, dass this auf die Instanz zeigt:

                  this.printout.delay(0.5);
                  // ...
                  printout: function() {
                     alert("Node#printout: " + this.no + ": " + this.nodearr);
                  }

                  Dass pcworld in dem Fall bind einsetzt, ist nötig, wie mein Beispiel zeigen sollte. PrototypeJS bindet hier nicht automatisch an die Instanz.

                  Du hast natürlich recht, dass es nicht nötig ist, vorsorglich für alle Methoden in Closures zu kapseln. Man sollte in dem Moment binden, wo es nötig wird, also konkret bei der Benutzung von delay, beim Event-Handling und sonstigen funktionalen Operationen:

                  this.printout.bind(this).delay(0.5);
                  elem.observe('click', this.printout.bindAsEventListener(this))
                  usw.

                  Grüße,
                  Mathias

                  1. Bei der printout-Methode ist es jedoch nötig, denn pcworld ruft diese mit delay() auf und erwartet, dass this auf die Instanz zeigt:

                    Habe ich auch nie bestritten.

                    Dass pcworld in dem Fall bind einsetzt,

                    ... hat er aber nicht. Insofern verstehe ich diesen Thread nicht, mir ging es darum zu hinterfragen warum er bind() an dieser spezifischen Stelle einsetzt.

                    Du hast natürlich recht, dass es nicht nötig ist, vorsorglich für alle Methoden in Closures zu kapseln.

                    Und es ist sogar gefährlich, da du nicht mehr den wahren Kontext siehst. Ein Aufruf instanz.methode() könnte dann unter Umständen in der Funktion ein anderes Objekt als 'instanz' benutzen. Sowas kann stundenlange fehlersuche bedeuten.

                    Struppi.

  2. var arr = new NodeArray();

    var n = new Node(arr, 1); // Knoten für die zweite Schicht erzeugen...
    n.addNode(new Node(n, 2)); // ...und hinzufügen
    arr.addNode(new Node(arr, 3));
    arr.addNode(n);
    arr.addNode(new Node(arr, 4));

      
    Nachdem ich einmal drüber geschlafen habe und mir dann wieder den Code etwas zu Augen geführt habe...  
    Ich muss die Variable n zuerst zum NodeArray hinzufügen, bevor ich einen Kindknoten hinzufügen kann, sprich so:  
    ~~~javascript
    var arr = new NodeArray();  
    var n = new Node(1);  
    arr.addNode(n); // gleich zum NodeArray hinzufügen  
    n.addNode(new Node(2)); // dann erst Kindknoten hinzufügen  
    arr.addNode(new Node(3));  
    arr.addNode(new Node(4));
    

    Wie ewig man doch von so einer Kleinigkeit aufgehalten werden kann! :)