Rolf B: Bildauswahl Reset

Beitrag lesen

Warnung an diejenigen, die keine Programmierstil-Enthusiasten sind: Haarspalterei voraus!

Hallo Felix,

ich habe großen Respekt vor deiner Kompetenz und Erfahrung, aber ich glaube trotzdem, dass Dir hier ein Irrtum rausgerutscht ist und Du Closure mit Scope verwechselst.

Allgemein entsteht eine Closure, wenn ein Funktionsobjekt erzeugt wird und der Code der Funktion auf Variablen zugreift, die außerhalb der Funktion deklariert wurden. Die Closure ist das Bündel, das diese Variablen „rettet“, falls das Funktionsobjekt seinen erzeugenden Scope überlebt. Wie das technisch abläuft, ist ein Implementierungsdetail und hier unwichtig. Aber eins ist wichtig: Die Parameter und lokalen Variablen der Funktion sind nicht Teil der Closure. Können sie auch nicht sein, denn die Variablen, die in der Closure enthalten sind, leben so lange weiter wie das Funktionsobjekt besteht. Die lokalen Variablen hingegen leben gerade so lange, wie ein Aufruf dieser Funktion dauert (es sei denn, natürlich, dass dieser Funktionsaufruf eine weitere Closure erzeugt…)

Im konkreten Fall besteht überhaupt kein Bedarf für eine Closure. Der Schleifenrumpf greift nur auf gallery zu, der bei mir die foreach-Laufvariable ist und bei Dir der Callback-Parameter. banner und thumb1 sind lokal, und hier stimme ich Dir bei: const ist richtig, let war falsch.

Durch die Funktion sind banner und thumb1 im lokalen Scope der Callback-Funktion eingeschlossen. Das ist gut. Wenn ich mit var gearbeitet hätte, dann wären banner und thumb1 bei mir nicht lokal gewesen, sondern im globalen Scope. Das wäre schlecht.

Aber: das habe ich nicht. Ich habe let verwendet, und let hat – wie const auch – nicht Funktionsscope, sondern Blockscope. Damit sind auch bei mir banner und thumb1 lokal im Schleifenrumpf.

Meine Variable galleries (die auch lieber ein const geworden wäre) ist übrigens nur der Einsteiger-Lesbarkeit wegen drin, ich hätte den zugewiesenen Wert auch direkt hinter dem of notieren können.

Was ist nun der Unterschied zwischen .forEach und for...of?

  • .forEach muss pro Durchlauf eine Callbackfunktion aufrufen. Für for...of muss hingegen ein Iteratorobjekt erzeugt und dieser Iterator durchlaufen werden. Der Performanceunterschied dürfte sich im Nanosekundenbereich bewegen, ich habe es nicht gemessen.
  • Wie schon diskutiert: der .forEach-Callback ist eine Funktion und kapselt damit auch var-Variablen. for...of tut das nicht. Vermeidet man var, wird das irrelevant.
  • .forEach benötigt voneinander entfernte Klammerpaare, die balanciert bleiben müssen. Die for...of-Schleife hat dagegen die runden Klammern beieinander und nur die geschweiften Klammern sind voneinander entfernt.
  • .forEach liefert außer dem Schleifenwert auch den aktuellen Schleifenindex und das iterierte Objekt als weitere Parameter. Wenn man das braucht, hat .forEach Vorteile. Braucht man es nicht, ist es unnötiger Overhead.
  • Dadurch, dass .forEach den Schleifenrumpf als Funktion erwartet, kann man den Rumpf einmal programmieren und für mehrere Schleifen wiederverwenden. Aus meiner Sicht wenig nützlich und ggf. sogar verwirrend. Rumpf und Schleife gehören normalerweise zusammen. Heißt: wenn ich den Schleifenrumpf wiederverwenden will, kann ich auch das for...of mit in die Funktion schieben und die zu durchlaufende Kollektion als Argument übergeben.
  • .forEach ist funktional und for...of ist prozedural. Ob das vor- oder nachteilig ist, ist eine Sache des persönlichen Geschmacks und des Stils, in dem die gesamte Anwendung erstellt wird.

Deswegen finde ich for...of für iterierbare Kollektionen heute die bessere Lösung. Die .forEach-Methode hat Nutzen, wenn eine Kollektion keinen Iterator hat, oder wenn ich die zusätzlichen Parameter des .forEach-Callbacks brauche.

Rolf

--
sumpsi - posui - obstruxi