Maurice: Mehrdimensionales Array verhält sich fraglich

Hallo Leute, ich könnte noch mals etwas Hilfe bei der Javascript Logik gebrauchen ;)

Ich habe folgenden beispiel Script:

  
  
function funktion(wert1, wert2, wert3) {  
   alert(wert1+wert2+wert3); /* ALERT 2 */  
   }  
  
var wert = new Array();  
  wert[0] = new Object();  
   wert[0]['wert1'] = 'wert1';  
   wert[0]['wert2'] = 'wert2';  
   wert[0]['wert3'] = 'wert3';  
  
for(var i = 0; i < wert.lenght; i++) {  
   setTimeout(function() { funktion(wert[i]['wert1'], wert[i]['wert2'], wert[i]['wert3']); }, 30);  
   alert(wert[i]['wert1'] + ' ' + wert[i]['wert2'] + ' ' + wert[i]['wert3']); /* ALERT1 */  
   }  
  

Der richtige Code ist etwas umfangreicher ;)
Es entstehen folgende Fehler:

Firebug meldet: (Zeile bei setTimeout) TypeError: wert[i] is undefined.
Der "ALERT 1" zeigt alle werte komplett und korrekt an.
Der "ALERT 2" wird nicht erreicht.

Ich suche schon seid Stunden nach dem Problem aber leider ohne auch nur den geringsten Erfolg :(
Vielleicht kennt jemand von euch dieses Problem und kann mir helfen.

Vielen Dank!

  1. Hakuna matata!

    Firebug meldet: (Zeile bei setTimeout) TypeError: wert[i] is undefined.
    Der "ALERT 1" zeigt alle werte komplett und korrekt an.
    Der "ALERT 2" wird nicht erreicht.

    Das Problem ist, dass die Variable i dem Zeitpunkt, zu dem die verzögerte Funktion "funktion" ausgeführt wird, nicht mehr den Wert des aktuellen Schleifendurchlaufs hat, sondern genau gleich wert.length ist. In unserer Doku findest du etwas zu den Hintergründen und auch gleich ein paar Lösungsvorschläge.

    Mein Vorschlag wäre, die Funktion an die Parameter zu binden, also:

    for(var i = 0; i < wert.lenght; i++) {  
       setTimeout(funktion.bind(window,wert[i]['wert1'], wert[i]['wert2'], wert[i]['wert3']), 30);  
    }
    

    Vielleicht kennt jemand von euch dieses Problem und kann mir helfen.

    Ziemlich sicher, darauf stößt jeder JavaScript-Entwickler irgendwann ;)

    --
    “All right, then, I'll go to hell.” – Huck Finn
    1. Lieber 1UnitedPower,

      Mein Vorschlag wäre, die Funktion an die Parameter zu binden, also:

      for(var i = 0; i < wert.lenght; i++) {

      setTimeout(funktion.bind(window,wert[i]['wert1'], wert[i]['wert2'], wert[i]['wert3']), 30);
      }

        
      der Umgang mit .bind() ist aber schon hohe Magie. Meiner Meinung nach liest sich soetwas auch nicht besonders intuitiv. Besser finde ich da eine Iteration mit einer Callback-Funktion:  
        
      ~~~javascript
      wert.forEach(function (w) {  
          setTimeout(funktion(w['wert1'], w['wert2'], w['wert3']), 30);  
      });
      

      Liebe Grüße,

      Felix Riesterer.

      --
      "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
      1. Hakuna matata!

        Ich halte sowieso nicht viel von Schleifen, bin also ganz auf deiner Seite was forEach angeht. Wenn man dein Beispiel allerdings mal syntaktisch bereinigt, sieht man schnell, dass das .bind() hier trotz Allem eine Existenzberechtigung hat.

        wert.forEach(function (w) {

        setTimeout(funktion(w['wert1'], w['wert2'], w['wert3']), 30);
        });

          
        ~~~javascript
        wert.forEach(function (w) {  
           setTimeout(function(){funktion(w['wert1'], w['wert2'], w['wert3'])}, 30);  
        });
        

        Um die Callback-Funktion muss noch ein closure gewrapt werden, die bind-Methode abstrahiert genau dieses verhalten:

        wert.forEach(function (w) {  
           setTimeout(funktion.bind(window,w['wert1'], w['wert2'], w['wert3']), 30);  
        });
        
        --
        “All right, then, I'll go to hell.” – Huck Finn
        1. Lieber 1UnitedPower,

          wert.forEach(function (w) {

          setTimeout(function(){funktion(w['wert1'], w['wert2'], w['wert3'])}, 30);
          });

            
          AAAargh, mein Beispiel war falsch! Ja, das kommt davon, wenn man es nicht testet... Ich hatte einen Funktionsaufruf notiert, anstatt eine Funktion (oder ein Funktionsobjekt). Danke für die Richtigstellung!  
            
          Liebe Grüße,  
            
          Felix Riesterer.
          
          -- 
          "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
          
          1. Vielen Dank die Sache funktioniert so.

            Aber dabei habe ich noch offene Fragen.

            • Wozu dient in diesem Fall das Window Objekt?
            • Ist eine forEach schleife technisch besser als eine for schleife? Wo ist der direkte Unterschied (Außer in der besseren Handhabung)
            1. Hakuna matata!

              • Wozu dient in diesem Fall das Window Objekt?

              Funktionen können bekanntermaßen über das this-Schlüsselwort auf einen Kontext zugreifen. Im Normalfall greifen Objekt-Methoden so auf ihre Objektinstanz zu. Wenn man mit Function.prototype.bind() eine Funktion an Parameter bindet, erzeugt man eigentlich eine neue Funktion. Dieser neuen Funktion muss man mitteilen, auf welches Objekt sich das this-Keyword beziehen soll. Wenn es für die Funktion uninteressant ist, weil sie das this-Keyword nicht benutzt, übergibt man wahlweise null oder window, damit drückt also nur aus, dass der Kontext in diesem Zusammen nicht weiter wichtig ist.

              • Ist eine forEach schleife technisch besser als eine for schleife? Wo ist der direkte Unterschied (Außer in der besseren Handhabung)

              for ist eine Schleife. Bei einer for-Schleife musst du selber die Lokik implementieren, WIE über das Array iteriert werden soll. Deshalb braucht man die Zählvariable i, die man selber initialisieren, hochzählen und mit dem Maximum vergleichen muss. Dabei ergeben sich natürlich viele Varianten, man könnte auch runterzählen oder nur bis zur Hälfte zählen. Außerdem könnte man die Zählvariable auch im Schleifenkörper noch verändern. Wenn man beim Lesen eines Quelltextes über eine for-Schleife stolpert, kann man deshalb nicht sofort sagen, WAS die Schleife eigentlich macht, dazu muss man sich den kompletten Schleifenkopf und Schleifenkörper anschauen.

              Array.prototype.forEach() ist eine Methode. Dabei ist sofort klar WAS die Methode macht. Sie iteriert nämlich über ein Array, über das WIE muss sich der Programmierer keine Gedanken mehr machen. Wenn man beim Lesen eines Quelltextes über einen Aufruf von forEach() stößt, kann man deshalb sofort sagen, WAS da passiert. Im Gegensatz zur for-Schleife ist der Code also selbsterklärend.

              Außerdem ist forEach im Gegensatz zur for-Schleife eine Funktion, und Funktionen kann man als Parameter an andere Funktionen übergeben, Funktionen können Funktionen zurückgeben, man kann sie kombinieren, ausführen oder sogar nur teilweise ausführen. Mit for-Schleifen geht das alles nicht.

              --
              “All right, then, I'll go to hell.” – Huck Finn
              1. Vielen Dank für die ausführliche Hilfe! :)