mirror of
https://codeberg.org/puppe/mpuppe.de-blog-posts.git
synced 2025-12-20 01:12:17 +01:00
Import old blog posts
This commit is contained in:
commit
8084c1e15f
23 changed files with 1384 additions and 0 deletions
181
posts/2012-08-03-closures-in-javascript.markdown
Normal file
181
posts/2012-08-03-closures-in-javascript.markdown
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue