JavaScript - Eine Einführung (Stefan Bosse) [04.2025] |
JavaScript ist dadurch sehr portabel und besitzt praktisch keine Abhängigkeiten zu der Rechnerarchitektur und dem Betriebssystem und ggfs. dem Browser!
Werte in JavaScript können sein:
true
und false
(𝔹)"text"
oder 'text'
(beide Varianten sind isomorph)Zusammengesetzte Werte (s.u.):
[1,2,3]
{x:1,y:2}
▸
|
✗
≡
|
Variablen besitzen keinen Datentyp sondern sind polymorphe Referenzen auf Werte:
var p;
p=0;
p=[1,2,3]
print(typeof p)
Folgende Grunddatentypen können Werte besitzen:
Funktionen sind Werte erster Ordnung (λ Ausdrücke, d.h. hier anonyme Funktionen), die keinen, einen, oder mehrere Parameter (lokale Funktionsvariablen) besitzen können:
function (par1, par~2~?, ..) { .. return ε }
var x,y,z=expr;
Achtung: Variablen können einfach on-the-fly durch eine Zuweisung ohne var Schlüsselwort definiert werden. Das sollte vermieden werden da es sich dann um globale (überall sichtbare) variablen handelt! Aber: Um Daten zwischen den Snippets zu teilen müssen globale Variablen verwendet werden.
operand1 + - / * % & && | ^ || operand2
- ! ~ operand
f(arg1,arg2,..)
Zeichenketten sind unveränderlich und werden zwischen Hochkommatas definiert 'character'
oder "character"
Im folgenden Beispiel soll noch eine globale Variable result mit dem Wert true hinzugefügt werden...
▸
|
✗
≡
|
▸
|
✗
≡
|
undefined
. Eine Variable die mit var definiert wurde trägt zunächst immer den "definierten" Wert undefined!!!Wichtig: Eine nicht definierte Variable (also niemals mit var oder über eine Zuweisung eingeführt) liefert entweder einen Fehler wen sie gelesen wird (wie hier im Notebook) oder den Wert undefined!
typeof
kann der Datentyp des aktuellen Wertes einer Variable abgefragt werden.Arithmetical: + - * / %
Boolean: && ||
Logical: & | ^
Relational: == < > <= >= !=
Achtung: Wird eine Variable ohne Definition mittels der var Anweisung benutzt (auf der linken Seite einer Zuweisung), wird immer einer globale Variable erzeugt!
▸
|
✗
≡
|
Frage 1. Gibt es im folgenden Beispiel die Variable x zweimal?
▸
|
✗
≡
|
function name (parameter1?, parameter2?, ..) {
return expr
}
Folgende Funktion berechnet die mathematische Fakultät über das Konzept der Rekursion:
▸
|
✗
≡
|
Aufgabe 2. Implementiere die Fibonacci Funktion (siehe auch nachfolgenden Abschnitt über bedingte Verzweigungen)
▸
|
✗
≡
|
ZnVuY3Rpb24gZmliKG4pIHsKICBpZiAobj4xKSByZXR1cm4gZmliKG4tMSkrZmliKG4tMikKICBlbHNlIHJldHVybiAxCn0KcHJpbnQoZmliKDUpKQ==
In JS lassen sich Funktionen programatisch übersetzen. Dazu können die eval(string)
oder new Function(string,...)
Funktionen verwendet werden. Problematisch ist der Kontext den die Funktionen bei der Evaluierung sehen, die weclhe Bindungen sie zu äußeren Funktionen und Variablen durchführen können.
▸
|
✗
≡
|
Diese Eigenschaft erlaubt die dynamische Erweiterung des Programmkontext eines Programms zuir Laufzeit (sowohl Daten als auch Kode). Man spricht auch von Deserialisierung (Programmtext ist eine Serie von Zeichen) in ausführbaren Kode.
Funktionen sind Objekte. Funktionen könnwn wieder in den ursprünglichen Quelltext gewandlet werden in dem die toString()
Methode auf das Funktionsobjekt angewendet wirde. Ein wichtige Eigenschaft für dynamische und apative Programmiering sowie mobilen Programmkode!
▸
|
✗
≡
|
Funktionen sind Werte erster Ordnung. Daher werden auch anonyme Funktionen, sprich lambda Ausdrücke, unterstützt.
Lambda Ausdrücke (Arrowfunctions)
▸
|
✗
≡
|
Anonyme Funktionen function (x) { }
und lambda Ausdrücke (x) => {}
besitzen unterschiedliches Verhalten in JS was die Selbstreferenz this
und ggfs. Bindung zu lokalen Variablen betrifft! "Normale" Funktionen binden die Selbstreferenz this
, Lambda Ausdrücke nicht!
this
▸
|
✗
≡
|
""
! Die letzten drei Werte entsprechen false! if (expression) { is true }
if (expression) { is true } else { is false }
Der else Zweig ist optional und wird nur dann ausgeführt wenn die Bedingung falsch (0) ist.
if-else Anweisungen "kaskadiert" (verkettet) werden, d.h. if-else-if-else..
Bedingungen werden z.B. mit relationalen Operationen getestet:
▸
|
✗
≡
|
Frage 3. Was passiert wenn man if (x>0) print
in x=1; if (--x>0) print
abändert?
switch (expression) {
case v1: .. break;
case v2: .. break;
..
default: ..
}
Nach jedem Fall muss eine break
Anweisungen folgen (bzw. Abschluss eines Falls), sonst werden die nachfolgenden Fälle eines ausgewählten Falls auch ausgeführt ("fall through behaviour"). Es sei denn dieses Verhalten ist erwünscht.
▸
|
✗
≡
|
Aufgabe 4. Ändere die nachfolgende if-else Auswahlsequenz in eine switch-case Anweisung um.
var y
if (x==0) y=1;
else if (x==1) y=2;
else if (x==2) y=2;
else if (x<0) y=-1;
else if (x>0) y=0;
Transformation von gekoppelten bedingten Verzweigung in eine Mehrfachauswahl
▸
|
✗
≡
|
PGI+dmFyPC9iPiB5CjxiPnN3aXRjaDwvYj4gKHgpIHsKICA8Yj5jYXNlPC9iPiAwOiB5PTE7IDxiPmJyZWFrPC9iPjsKICA8Yj5jYXNlPC9iPiAxOgogIDxiPmNhc2U8L2I+IDI6IHk9MjsgPGI+YnJlYWs8L2I+OwogIGRlZmF1bHQ6CiAgICA8Yj5pZjwvYj4gKHg8MCkgeT0tMTsKICAgIDxiPmVsc2U8L2I+IDxiPmlmPC9iPiAoeD4wKSB5PTA7Cn0=
Die Transformation von gekoppelten bedingten Verzweigung in eine Mehrfachauswahl ist nicht immer möglich, aber immer eine Abbildung einer Mehrfachauswahl in gekoppelten bedingten Verzweigung.
for (init;test;change) {
..
}
for (i=a;i<b;i++) {
..
}
Aufgabe 5. Implementiere eine Zählschleife mit der Zählervariable i die den Zählerindex (den Wert von i) aufsummiert und am Ende ausgibt (Summenberechnung). Die Zählervariable soll Werte von {3,5,7,9,11} annehmen.
Summenbildung mit Zählscheifen
▸
|
✗
≡
|
PGI+dmFyPC9iPiBzdW09MDsKPGI+Zm9yPC9iPiAodmFyIGk9MztpPD0xMTtpKz0yICkgewogIHN1bSs9aQp9CnByaW50KHN1bSk7
while (condition) {
..
}
{
..
} do (condition)
Frage 6. Worin unterscheiden sich die while und die do Schleifen?
Bedingte Schleifen
▸
|
✗
≡
|
Aufgabe 7. Zählschleifen sind in JS eigentlich auch nur bedingte Schleifen. Übertrage folgende Zählschleife in eine while und do Schleife.
Schleifentransformation
▸
|
✗
≡
|
PGI+dmFyPC9iPiBzdW09MDsKPGI+dmFyPC9iPiBpPTA7CjxiPndoaWxlPC9iPihpPDEwKSB7CiAgc3VtPXN1bStpOwogIGkrKzsKfQo8Yj52YXI8L2I+IHN1bT0wOwo8Yj52YXI8L2I+IGk9MDsKPGI+ZG88L2I+IHsKICBzdW09c3VtK2k7CiAgaSsrOwp9IDxiPndoaWxlPC9iPiAoaTwxMCk=
Frage 8. Kann man immer eine for in eine do Schleife transformieren? Warum geht folgendes Beispiel schief?
Schleifentransformation - failed
▸
|
✗
≡
|
CjxiPnZhcjwvYj4gc3VtPTA7CjxiPnZhcjwvYj4gaT0xMDA7Ci8vPGk+IGZhbHNjaCEgZG8gd2lyZCBtaW5lc3RlbnMgZWlubWFsIGR1cmNobGF1ZmVuPC9pPgoKPGI+ZG88L2I+IHsKICBzdW09c3VtK2k7CiAgaSsrOwp9IDxiPndoaWxlPC9iPiAoaTwxMCkKLy88aT4gcmljaHRpZyE8L2k+Cgo8Yj53aGlsZTwvYj4gKGk8MTApIHsKICBzdW09c3VtK2k7CiAgaSsrOwp9
Arrays können mehrsortig sein, d.h. die Elemente eines Arrays können von beliebigen Datenyp sein!
[1,2,3]
[1,true,'test']
[{x:1},{x:2},{y:1}]
Erzeugung eines Arrays mit Werten durch eckige Klammerpaare [v1,v2,..]
Arrays von Arrays sind auch möglich (Matrizen), z.B. [[1,2],[3,4]]
Zugriff auf Arrayelement durch Indexoperator array[index]
, ebenso Wertzuweisung möglich.
Bei mehrdimensionales Arrays array[row][col]
Erstes Element eines Arrays hat immer den Index 0!
▸
|
✗
≡
|
array.length()
mappedarray = array.map(function (v,index) { return expr })
filteredarray = array.filter(function (v,index) { return true or false })
array.push(v)
last = array.pop()
copy = array.slice()
sub = array.slice(a,a+n);
revarray = array.reverse()
▸
|
✗
≡
|
▸
|
✗
≡
|
Aufgabe 9. Dynamische Erzeugung von Arrays: Implementieren Sie eine Funktion die die ersten Fibonacci Zahlen im Bereich [1,9] berechnet und als Array zurück gibt. Nutze eine Zählschleife for, keine Funktionsrekursion!.
Dynamische Arrays am Beispiel der Berechnung einer Sequenz von Fibonacci Zahlen
▸
|
✗
≡
|
PGI+dmFyPC9iPiBmaWJzPVtdOwovLzxpPiBUT0RPIGZvci1sb29wIGZpYm9uYWNjaTwvaT4KCjxiPmZvcjwvYj4odmFyIGk9MTtpPDEwO2krKykgewogIDxiPmlmPC9iPiAoaT09MSkgZmlicy5wdXNoKDEpOwogIDxiPmVsc2U8L2I+IGZpYnMucHVzaChmaWJzW2ktMV0rZmlic1tpLTJdKQp9CnByaW50KGZpYnMp
{
attribute : value,
attribute : value,
..
}
Dabei sind die Attribute Elementnamen die beliebige Werte besitzen können (veränderlich). Tatsächlich ist jedes Objekt eine Hashtabelle, und der Attributname ist der Schlüssel.
Auf ein Element (Attribut) eines Objekts wird durch den Punktoperator sowohl lesend als auch schreibend zugegriffen record.attribute
Werte von Attributen können Funktionen und weitere Datenstrukturen sein, so dass sich eine Punktoperatorkette ergibt record.selector.selector.
▸
|
✗
≡
|
Arrays sind in javaScritpt mehrsortig, d.h. jedes Element kann einen Wert unterschiedlichsten Datentyps enthalten
Daher können Arrays und Rekords (rein Datenstrukturen) gemischt werden
Elemente von komplexen Strukturen mit Arrays können durch eine Kette von Dereferenzierungen erreicht werden s.x.y[i].z
▸
|
✗
≡
|
Da auch Funktionen als Werte in Objekten gebunden werden können (und i.A. mittels this
auf das Objekt zugreifen können) sind in JS Objekte universell funktional einsetzbar.
-- Definition der Konstruktorfunktion --
function C(parameter1,..) {
this.x1=expression;
..
}
-- Definition der Prototypen --
C.prototype.method1 = function (parameter1,..) {
this.x1=..
}
C.prototype.method2 = function (par1,..) {
this.x1=..
}
..
-- Instanziierung eines Objekts von der Konstruktirfunktion --
var obj = new C(argument1,..);
obj.methode1(..)
obj.methode2(..)
..
Der Vorteil von Prototypenfunktionen ist die Trennung von Daten und Funktionen, d.h. die Prototypenfunktionen gehören nicht direkt zum Objekt sondern werden referenziert und dynamisch gebunden.
▸
|
✗
≡
|
Agenten können programmatisch als Objekte von Klassen angesehen werden. Eine Klasse definiert Daten und Verhalten von Agenten über Funktionen.
Aufgabe 10. Nehmen wir an dass ein sehr einfacher Agent ein endlicher Zustandsautomat ist. Ein Agent habe drei Kontrollzuständen z1, z2, z3. Jeder Zustand ist mit einer Aktion a1, a2, a3 verbunden. D.h. die Aktivierung eines Zustands führt die Aktion, hier eine Funktion, aus. Es gibt eine Sensorvariable x die von außen verändert wird. Der Agent (Automat) soll die Sequenz von x Werten 1,2,3 erkennen und am Ende der Sequenz (also in Zustand z3) die Variable y auf den Wert 1 setzen. Es wird noch ein Zustandszeiger s benötigt, der die nächste Aktivität auswählt (Funktionsreferenz). Schließlich soll es eine next Funktion geben die die nächste Aktivität aufruft, also s()
, alles in dem funktionalen Objekt enthalten. Jede Aktivitätsfunktion gibt die nächste Aktivität zurück (return
).
▸
|
✗
≡
|
YWdlbnQgPSB7CiAgeDowLAogIHk6MCwKICBhMTogPGI+ZnVuY3Rpb248L2I+ICgpICB7IDxiPmlmPC9iPiAodGhpcy54PT0xKSA8Yj5yZXR1cm48L2I+IGEyOyA8Yj5lbHNlPC9iPiA8Yj5yZXR1cm48L2I+IGExIH0sCiAgYTI6IDxiPmZ1bmN0aW9uPC9iPiAoKSAgeyA8Yj5pZjwvYj4gKHRoaXMueD09MikgPGI+cmV0dXJuPC9iPiBhMzsgPGI+ZWxzZTwvYj4gPGI+cmV0dXJuPC9iPiBhMiB9LAogIGEzOiA8Yj5mdW5jdGlvbjwvYj4gKCkgIHsgPGI+aWY8L2I+ICh0aGlzLng9PTMpIHsgdGhpcy55PTE7IDxiPnJldHVybjwvYj4gYTMgfSB9LAogIG5leHQgOiA8Yj5mdW5jdGlvbjwvYj4gKCkgeyB0aGlzLnM9dGhpcy5zKCkgfQp9
Math.sin(rad)
Math.cos(rad)
Math.tan(rad)
Math.pow(x,e)
Math.log(x)
Math.sqrt(x)
Math.abs(x)
Math.random()
▸
|
✗
≡
|