Home » 2021 » September (Seite 2)

Archiv für den Monat: September 2021

IIFE in JavaScript

Immediately invokes function expressions: Das sind Funktionsdefinitionen, welche durch das hinten Anhängen eines () sofort aufgerufen werden.

function(){console.log("Schon ausgeführt!";}(); 

Hier ein interessanter Anwendungsfall einer IIFE:

Dieses script.js file zeigt einen anwendungsfall einer IIFE. Dem ‚onclick‚-Ereignis des Buttons wird eine Funktion ‚countAlerter‚ zugeordnet. (Auf den ersten Blick scheint es zwar, die äussere, anonyme Funktion werde zugeordnet, was nicht stimmt, denn diese wird ja direkt mit ‚()‚ aufgerufen und der Rückgabewert dieses Aufrufs wird onclick zugordnet.) Da countAlerter aber innerhalb einer enclosing Function definiert ist behält die countAlerter die darin enthaltene (Objekt-)Variable ‚clickCount‘ in seiner Closure.

let button = document.getElementById("b1")
button.onclick = function(){
    let clickCount = 0;
    return function countAlerter(){
        alert('Click nbr ' + clickCount++);
    }
}();

Diese Funktion gibt also bei jedem Klick auf den Button die bereits getätigte Anzahl Klicks aus.

JavaScript – Functions Scope / This

Closures – Function Scope

Mit einer Funktion lässt sich so etwas wie eine Klassendefinition erstellen. Wird von der Funktion eine (eingebettete) Funktion zurückgegeben, dann hat diese den ganzen scope ihrer umgebenden Funktion erhalten:

function remember(number){
   return function(){
      return number;
   }
}
f1 = remember(99);
f1();
//99

Vorsicht bei Übergabe von Funktionen!!

Im Gegensatz zu Java können Funktionen manchmal ‚Instanz-Variablen‘ haben, manchmal aber auch nicht. Auf was die This-Referenz der Funktion zeigt kann verwirrend unterschiedlich sein.
Im obigen Beispiel ist f1 eine „Objekt-Funktion“, d.h. eine Funktion mit einem Funktionspezifischen Scope (Java-Entwickler würden von einer Instanz sprechen).

Keinen non-global Closure hat hingegen eine Funktion, die zwar innerhal eines Objektes definiert ist, die aber dann (ohne umgebendes Objekt) als Referenz über geben wird:

> const dog = {
... age: 5,
... growOneYear: function(){
..... this.age += 1;
..... }
};
undefined
> dog.growOneYear();
undefined
> dog.age;
6
> function invokeTwice(cb){
... cb();
... cb();
}
undefined
> invokeTwice(dog.growOneYear);
undefined
> dog.age;
6 //<-- Hinaufzählen hat nicht funktioniert!

Wie man sieht hat die Funktionsreferenz dog.growOneYear das age Feld nicht in seinem Scope mit genommen!

Anders sieht es wiederum aus, wenn man eine Funktion mitgiebt, die durch eine andere Funktion erstellt wurde und deshalb deren Scope mit sich mit schleppt:

/* Hauptfunktion wird aufgerufen und gibt Unterfunktion zurück, die aber den Clusure hat der den Block der Hauptfunktion umfasst! */
> let catGrows = function(){
... age = 0;
... return () => {return age++;}
}(); 
undefined
> catGrows()
0
> catGrows()
1
> catGrows()
2
> invokeTwice(catGrows) /* Keine Ausgabe, aber intern wird gezählt */
undefined
> catGrows()
5
>

Das Beispiel mit dem „Function-in-Funktion-Scope“ funktioniert. Nur ist es äuserst unpraktisch, das man nur genau über eine Funktion zugriff auf das Objekt hat.

Nur wenn die Funktion auf dem Objekt aufgerufen wird, zeigt ihr This auf das Objekt:
>dog.growOneYear()

Damit invokeTwice das richtige Resultat liefert können wir auch programmieren:

> dog.age;
6
> invokeTwice(function(){dog.growOneYear();});
undefined
> dog.age;
8

gowOneYear operiert so immer auf dem mitgegebenen dog Objekt.



Die vielversprechendere Lösung ist es, via bind() Methode, der Funktion explizit das Objekt mitzugeben, auf das die This-Referenz der Funktion zeigen soll:



> objectBoundGrowOneYearFunction = dog.growOneYear.bind(dog);
[Function: bound growOneYear]
> invokeTwice(objectBoundGrowOneYearFunction);
undefined
> dog.age;
10
>