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ürfor...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 auchvar
-Variablen.for...of
tut das nicht. Vermeidet manvar
, wird das irrelevant. .forEach
benötigt voneinander entfernte Klammerpaare, die balanciert bleiben müssen. Diefor...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 dasfor...of
mit in die Funktion schieben und die zu durchlaufende Kollektion als Argument übergeben. .forEach
ist funktional undfor...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