© Visual Generation/Shutterstock.com
Die Besonderheiten von JavaScript im Überblick

JavaScript für Umsteiger


An JavaScript als Programmiersprache gibt es mittlerweile kaum einen Weg vorbei. Es ergibt für viele Programmierer also durchaus Sinn, wenn sie sich zumindest mit den Grundlagen beschäftigen. Umsteiger sollten sich ebenfalls zuerst mit den Grundlagen und Besonderheiten befassen.

JavaScript weist im Vergleich zu anderen Sprachen einige Besonderheiten auf, die teilweise beabsichtigt, aber zum Teil auch in der Entstehungsgeschichte der Sprache begründet sind. Die große Verbreitung und viele der Einsatzszenarien waren zum Entstehungszeitpunkt von JavaScript noch nicht absehbar. Das ist auch einer der Gründe, warum sich der Sprachkern in den vergangenen Jahren an einigen Stellen deutlich zum Positiven verändert hat. Diese Tatsache und die Existenz von Hilfsmitteln wie TypeScript, die auf JavaScript basieren, machen den Umstieg für Entwickler, die ursprünglich von anderen Programmiersprachen kommen, leichter. Es spielt keine Rolle, von welcher Programmiersprache Sie kommen – mit den grundlegenden Elementen von JavaScript müssen Sie sich immer beschäftigen, da auch Dialekte wie TypeScript auf derselben Basis aufsetzen.

Das Typsystem von JavaScript

Die Aussage, dass JavaScript über kein Typsystem verfügt, ist so nicht ganz richtig. Es gibt eine Reihe primitiver und komplexer Datentypen. Der Umgang mit diesen unterscheidet sich zum Teil jedoch gravierend von anderen Programmiersprachen. Die Basistypen in JavaScript sind:

  • Boolean

  • Number

  • String

  • Null, undefined

  • Symbol

Diese Basistypen werden by Value übergeben. Wenn Sie also eine Zeichenkette an eine Funktion übergeben, wird diese kopiert. Modifizieren Sie den Wert innerhalb der Funktion, ändert sich der Wert außerhalb nicht. Listing 1 verdeutlicht diesen Zusammenhang.

Listing 1: By-Value-Übergabe von Werten

let name = 'Klaus'; function doSomethingWithName(name) { name = 'Petra'; console.log(name); // Petra } doSomethingWithName(name); console.log(name); // Klaus

Unabhängig vom Effekt dieses Beispiels handelt es sich um schlechten Stil, wenn Sie die Argumente einer Funktion direkt manipulieren und sich auf eventuelle Seiteneffekte verlassen. Eine bessere Idee ist es, eine tiefe Kopie des Arguments anzufertigen, mit dieser weiterzuarbeiten und den veränderten Wert zurückzugeben. Das wird insbesondere dann interessant, wenn es um komplexe Datentypen wie Arrays oder Objekte geht, die by Reference übergeben werden. Das bedeutet, dass Sie an eine Funktion lediglich die Referenz auf das Objekt übergeben. Eine Änderung des Objekts wirkt sich auf das ursprüngliche Objekt aus und sorgt für einen, gegebenenfalls unbeabsichtigten Seiteneffekt. Wie sich das im Code auswirkt, sehen Sie in Listing 2.

Listing 2: By-Reference-Übergabe von Objekten

let person = { name: 'Klaus' }; function doSomethingWithPerson(person) { person.name = 'Petra'; console.log(person.name); // Petra } doSomethingWithPerson(person); console.log(person.name); // Petra

Eine tiefe Kopie eines Objekts können Sie auf mehrere Arten erzeugen. In den meisten Fällen sollten Sie jedoch auf eine etablierte Bibliothek zurückgreifen. Hier haben Sie die Wahl zwischen einfachen und leichtgewichtigen Lösungen wie Lodash und Immutability Helper oder umfangreichen Bibliotheken wie Immer und immutable. js. Hier sollten Sie sich je nach Anwendungsfall für die passende Bibliothek entscheiden.

Ansonsten handelt es sich bei JavaScript-Objekten um Key-Value Stores, deren Schlüssel immer Zeichenketten sein müssen und deren Werte beliebige Datentypen annehmen können. So können Sie einfache Eigenschaften mit primitiven Werten, aber auch untergeordnete Objekte erzeugen. Enthält eine Eigenschaft ein Funktionsobjekt, kann dieses als Methode des Objekts verwendet werden. Seit ECMAScript 6 gibt es eine Kurzschreibweise, bei der Sie das function-Schlüsselwort bei solchen Methoden weglassen können. Listing 3 enthält ein Beispiel hierfür.

Listing 3: Methoden-Shortcut

const klaus = { firstname: 'Klaus', lastname: 'Müller', getFullName() { return `${this.firstname} ${this.lastname}`; }, };

Arrays sind die mächtigsten Datenstrukturen, die im Kern von JavaScript enthalten sind. Hierbei handelt es sich um Objekte, die einen fortlaufenden numerischen Index als Schlüssel für die Eigenschaften aufweisen. Die Werte können Sie auch hier beliebig wählen. Neben diesen Basiseigenschaften verfügen Arrays über eine Reihe von Methoden, mit deren Hilfe Sie mit dem Array arbeiten können. So ist es möglich, über das Array zu iterieren, die Elemente mit der map-Methode umzuwandeln oder mit der some-Methode herauszufinden, ob eine bestimmte Bedingung für mindestens ein Element gilt.

Das lose Typsystem von JavaScript bietet eine große Flexibilität, die sich zahlreiche Entwickler nicht nehmen lassen möchten. Allerdings entsteht daraus auch eine Reihe von Problemen, so können Sie einer Variablen beispielsweise eine Zeichenkette als Wert zuweisen und zu einem späteren Zeitpunkt eine Zahl übergeben. Diese dynamischen Wechsel des Typs einer Variablen in JavaScript stellen keinen Fehler dar, die Verlässlichkeit ist gerade bei umfangreichen Applikationen an dieser Stelle jedoch eingeschränkt. Eine Lösung für dieses Problem bietet TypeScript. Diese Sprache fügt Typeninformationen und weitere Features wie Interfaces, Generics oder Enums zum Sprachumfang von JavaScript hinzu. Der TypeScript-Compiler führt, wie im Folgenden zu sehen, eine statische Überprüfung des Quellcodes durch und übersetzt den TypeScript-Code in JavaScript, das Sie im Browser oder serverseitig in Node.js ausführen können:

function add(a: number, b: number): number { return a + b; }

Scoping

Ein weiterer wichtiger Aspekt einer Programmiersprache ist der Gültigkeitsbereich von Variablen. JavaScript verfügt über vier Gültigkeitsbereiche:

  • Globaler Scope: Der globale Scope reicht in JavaScript am weitesten. Er umfasst die gesamte Applikation. Variablen, die im globalen Scope deklariert werden, stellen insofern eine Gefahr für die Applikation dar, da sie vom Garbage Collector der JavaScript Engine während der Laufzeit der Applikation nicht freigegeben werden können, weil immer eine Referenz auf den jeweiligen Speicherbereich existiert. Aus diesem Grund und um eventuelle Namenskonflikte zu vermeiden, sollten Sie möglichst sparsam mit globalen Variablen umgehen.

  • Funktions-Scope: In der ursprünglichen Version von JavaScript war der Funktions-Scope die kleinste Einheit, in der Sie eine Variable deklarieren konnten. Der Funktions-Scope umfasst eine Funktion und alle ihre Unterfunktionen.

  • Closure Scope: Der Closure Scope ist eine Erweiterung des Funktions-Scopes um den erstellenden Kontext der Funktion. Sie haben also nicht nur Zugriff auf die Funktion selbst, sondern auch auf den umliegenden Scope. Wird eine Funktion also innerhalb einer Funktion definiert und von dieser zurückgegeben, hat sie immer noch Zugriff auf den Scope der definierenden Funktion, auch wenn die Abarbeitung dieser Funktion bereits beendet ist. Mit diesem Mechanismus lassen sich Variablen definieren, auf die Sie nur über eine solche Funktion zugreifen können. Listing 4 enthält ein Beispiel hierfür.

  • Block-Scope: Der Block-Scope ist der jüngste Vertreter der JavaScript Scopes. Ein Block wird in JavaScript normalerweise durch geschweifte Klammern umschlossen. So stellt beispielsweise eine Funktion einen Block dar, aber auch eine if-Bedingung, eine for-Schleife oder ein try-catch-Statement sind Blöcke. Mit dem Block-Scope haben Sie eine wesentlich feinere und damit bessere Kontrolle über die Variablen Ihrer Applikation.

Listing 4: Private Variablen in JavaScript

function myPrivate() { let value = ''; // "private" variable return { setValue(v) { // schreibender Zugriff value = v; }, getValue() { // lesender Zugriff return value; }, }; } const myPriv = myPrivate();...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang