Import old blog posts

This commit is contained in:
Martin Puppe 2020-08-08 14:30:21 +02:00
commit 8084c1e15f
23 changed files with 1384 additions and 0 deletions

View file

@ -0,0 +1,181 @@
---
layout: post
title: "Closures in Javascript (und Scala)"
date: 2012-08-03 21:37
comments: true
categories: Javascript
keywords: "closures, javascript, scala"
---
Closures[^terminologie] sind meiner Meinung nach eines der
interessantesten Sprachkonstrukte von Javascript. Sie erlauben sehr
elegante Lösungen, die sonst nicht möglich wären. Was das konkret
bedeutet, werde ich an einem Beispiel erläutern. Zunächst möchte ich
aber erklären, was Closures sind.
## Was ist eine Closure?
**Eine Closure beinhaltet eine Funktion und (auch nicht-lokale)
Variablen, die innerhalb der Funktion referenziert werden.** Closures
verändern die Lebensdauer von Variablen. Schauen wir uns dazu ein
Beispiel an:
``` javascript
var x = 0;
function foo() {
var y = 42;
return x + y;
}
x = x + 1;
```
Wir sehen hier zwei Variablen, `x` und `y`. Im Hinblick auf die
Sichtbarkeit unterscheiden sich die Variablen darin, dass `y` nur
*innerhalb* der Funktion `foo` sichtbar ist. Was die Lebensdauer
betrifft, so existiert `y` nur während `foo` ausgeführt wird.
Sichtbarkeit und Lebensdauer für sich genommen sind keine schwierigen
Konzepte. Javascript ist allerdings eine funktionale Sprache und da wird
die Sache interessant. Javascript erlaubt es nämlich, Funktionen als
Rückgabewert anderer Funktionen zu verwenden. Dazu erweitern wir das
obige Beispiel.
``` javascript
function makeFoo() {
var x = 0;
function foo() {
var y = 41;
x = x + 1;
return x + y;
}
return foo;
}
var bar = makeFoo();
console.log(bar());
console.log(bar());
console.log(bar());
```
In der Konsole erhält man folgende Ausgabe:
```
42
43
44
```
Das ist auf den ersten Blick überraschend, wenn wir davon ausgehen, dass
lokale Variablen nur existieren, solange die Funktion, in der sie
definiert wurden, ausgeführt wird. Die Variable `x` hingegen bleibt
weiterhin innerhalb der Funktion `foo` bzw. `bar` sichtbar und behält
zudem ihren Zustand, obwohl `makeFoo` vollständig abgearbeitet wurde.
*`foo` wurde zusammen mit `x` in eine Closure gepackt*.
## Praktische Anwendung
Das obige Beispiel ist konstruiert und in der Praxis kaum nützlich.
Closures haben aber durchaus sinnvolle Anwendungen. Stellen wir uns
einmal vor, wir wollten verschiedene Objekte mit einer eindeutigen
Nummer (einer ID) versehen. Dazu wollen wir eine Funktion `generateId`
definieren, die stets eine andere Zahl zurückliefert. Der Einfachheit
halber zählen wir von 0 aufwärts. Wir haben mehrere Möglichkeiten dieses
Problem zu lösen.
### Möglichkeit 1: Globaler Zähler
``` javascript
var counter = 0;
function generateId() {
var id = counter;
counter = counter + 1;
return id;
}
var obj = {
id: generateId()
}
```
Ich halte diese Lösung für nicht sehr sinnvoll. `counter` ist eine
globale Variable. D.h. wir verschmutzen hier den globalen Namensraum mit
einer Variable, die nur für eine einzige Funktion benötigt wird.
Außerdem kann die Variable praktisch an jeder Stelle im Programm
versehentlich geändert werden. Und es wird überhaupt nicht klar, dass
counter` und `generateId` zusammengehören.
### Möglichkeit 2: Zähler und Funktion in einem Objekt
``` javascript
var idGenerator = {
counter: 0,
generateId: function() {
var id = this.counter;
this.counter = this.counter + 1;
return id;
}
};
var obj = {
id: idGenerator.generateId()
}
```
Diese Lösung ist schon etwas besser, in der Hinsicht, dass wir auf eine
globale Variable `counter` verzichten. Auch wird der Zusammenhang von
`counter` und `generateId` deutlicher. Allerdings ist diese Variante
unhandlich in der Verwendung (`idGenerator.generateId()`). Außerdem kann
auch hier `counter` an anderer Stelle im Programm geändert werden,
obwohl die Variable einzig und allein von der Funktion `generateId`
manipuliert werden sollte.
### Möglichkeit 3: Closure
``` javascript
var generateId = (function() {
var counter = 0;
return function() {
var id = counter;
counter = counter + 1;
return id;
};
})();
var obj = {
id: generateId()
}
```
Ich halte diese Lösung für die eleganteste. Der Zusammenhang von
`counter` und `generateId` wird deutlich und der Zähler ist außerhalb
der Funktion überhaupt nicht sichtbar.
## Anhang: Das Beispiel in Scala
Closures gibt es nicht nur in Javascript, sondern in wohl jeder
funktionalen Programmiersprache. Ich habe mich kürzlich im Rahmen einer
Vorlesung mit Scala beschäftigen dürfen, daher hier das obige Beispiel
in Scala:
``` scala
val generateId = (() => {
var counter = 0
() => {
var id = counter;
counter = counter + 1;
id
}
}).apply()
```
[^terminologie]:
Kurz zur Terminologie: Das deutsche Wort für „function
closure“ ist wohl „Funktionsabschluss“. Allerdings hat
man wenig Glück, wenn man danach googlet. Mir scheint der Begriff nicht
sehr gebräuchlich zu sein, also bleibe ich bei der englischen
Bezeichnung.