Sie sind auf Seite 1von 31

Seminararbeit im Kurs 1908 OOP-Sprachen und -Konzepte

Thema: Praxisorientierte Nutzung von Prototypen in JavaScript am Beispiel von jQuery

Autor: Robert Schweda Matrikelnummer: 8802440

Fernuniversit¨at Hagen

Seminararbeit zum Kurs 1908 - Thema: JavaScript

ii

Inhaltsverzeichnis

1 Einleitung

1

2 Prototypenbasierte Programmierung

 

1

2.1 Historische Einordnung

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

1

2.2 Prototypen in der objektorientierten Programmierung

 

2

2.3 Eigenschaften protoypenbasierter Programmiersprachen

 

3

2.3.1 Fehlen von Klassen und Konstruktoren

 

3

2.3.2 Slots und Parent-Slot

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

3

2.3.3 Delegation

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

4

2.3.4 Vielfalt prototypenbasierter Programmiersprachen

 

5

3 Prototypen in JavaScript

6

3.1 Geschichte JavaScripts

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

6

3.1.1 ECMAScript

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

6

3.1.2 JavaScript als missverstandene Sprache

 

7

3.1.3 JavaScript heute

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

7

3.2 Objekte in JavaScript .

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

7

3.2.1 Objektliterale

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

8

3.2.2 Konstruktoren

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

8

3.2.3 Klonung und Erweiterung

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

10

3.3 Prototype-Eigenschaft

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

10

3.3.1 Object.prototype

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

10

3.3.2 [[Prototype]] und Konstruktoren

 

11

3.4 Delegation in JavaScript

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

11

3.5 Vererbung in JavaScript

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

13

3.5.1 Pseudo-klassenbasierte Vererbung

 

14

3.5.2 Prototypenbasierte Vererbung

 

15

3.5.3 Mischformen .

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

17

3.5.4 Mixins als Alternative

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

18

4 JavaScript in der Praxis

18

4.1 Das jQuery-Projekt

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

19

4.2 Untersuchung ausgew ¨ahlter Quelltextpassagen

 

22

4.2.1 Initialisierung des jQuery-Objekts

 

22

4.2.2 Verwendung von Prototypen

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

24

4.2.3 Weitere Besonderheiten im Entwurf

 

24

4.2.4 Praxisbeispiel

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

25

Zuk unftige¨

5 Entwicklung und Fazit

 

26

6 Anhang

27

Seminararbeit zum Kurs 1908 - Thema: JavaScript

1

1 Einleitung

Diese Seminararbeit entstand im Rahmen des Kurses 1908 - OOP-Sprachen und - Konzepte“ an der Fernuniversit ¨at Hagen, und besch ¨aftigt sich mit der prototypenba- sierten Programmiersprache JavaScript. Nach einer kurzen theoretischen Betrachtung des Prototypenbegri s und der prototypenbasierten Programmierung im Allgemeinen, befasst sich der Hauptteil der Arbeit in den Kapiteln 3 und 4 mit einer konkreten Implementierung einer solchen Sprache in Form von JavaScript und ihrem praktischen Einsatz in Form der jQuery Bibliothek.

2 Prototypenbasierte Programmierung

Bevor das Themengebiet der prototypenbasierten Programmierung genauer untersucht wird, soll zun ¨achst die Entstehung des Begri s Prototyp“ vorgestellt werden. Inter- essierte Leser finden hierzu in [1] eine ausfuhrliche¨ Ausarbeitung, die im n ¨achsten Ab- schnitt kurz zusammengefasst wird.

2.1 Historische Einordnung

Die objektorientierte Programmierung ist eng mit dem Begri der Klasse verkn upft.¨ Der Ursprung der Kategorisierung und des Klassenbegri s selbst findet sich bereits in der Antike bei Platon wieder. Platon unterscheidet zwischen zwei Arten von Objekten, zum einen Objekte, die Eigenschaften von Dingen beschreiben (sog. Ideen, Formen ) und zum anderen real existierende Objekte, die diese Eigenschaften vereinen. Mit die- sem Sachverhalt beschreibt der griechische Philosoph im Wesentlichen die Beziehung zwischen Klassen und ihren Instanzen [2]. Sein Sch uler¨ Aristoteles formulierte mit der Definitionsregel genus proximum et dif- ferentia specifica“ die Prinzipien der Generalisierung und Spezialisierung, wie man sie auch heute in der objektorientierten Programmierung wiederfindet. Nach Aristoteles wird ein neuer Begri durch einen Oberbergri (genus proximum ) und einer Menge von Eigenschaften, die ihn von diesen abgrenzen, beschrieben ( di erentia specifica ) [3]. Kritik an dieser Form der Kategorisierung wurde im 20. Jahrhundert durch den Philo- sophen Ludwig Wittgenstein ge ¨außert. Wittgenstein stellte fest, dass es gewisse Dinge gibt, die nicht klar kategorisiert werden k ¨onnen. Als Beispiel bringt er den allgemeinen Begri des Spiels“. Spiele verf ugen¨ uber¨ eine Menge von Verwandtschaften, die Witt- genstein als Familien¨ahnlichkeiten bezeichnet. Jedes individuelle Spiel verfugt¨ uber¨ Eigenschaften, durch die es sich als Spiel auszeichnet, es existiert allerdings keine ge- meinsame Eigenschaft, uber¨ die alle Spiele verf ugen¨ [4, S. 56-68]. In den Siebzigerjahren setzte Elenoar Rosch die Forschung auf dem Gebiet der Fami- lien ¨ahnlichkeiten fort. Sie stellte fest, dass es Entit ¨aten gibt, die eine Klasse besser repr ¨asentieren als andere. Ein Singvogel ist beispielsweise ein typischerer Vertreter der Kategorie Vogel als ein Pinguin oder ein Huhn. Diese klassenspezifischen Vertreter bezeichnete sie als Prototypen [5].

Seminararbeit zum Kurs 1908 - Thema: JavaScript

2

2.2 Prototypen in der objektorientierten Programmierung

Wie Objekte in der realen Welt beschrieben werden, wirkt sich unmittelbar auf den Ent- wurf von Programmiersprachen aus, die mit diesen Objekten arbeiten mussen.¨ Wie bei der Kategorisierung anhand von Klassen in der Philosophie, existiert auch im Bereich der klassenbasierten Programmierung Kritik an diesem Konzept. Die beiden wesentli- chen Punkte dabei lauten nach [6] und [7, S. 92]:

Die Erstellung einer Klassenhierarchie ist ein kreativer Vorgang, der zu vielen verschiedenen L ¨osungen f uhren¨ kann. Dabei kann es sogar sein, dass eine L¨osung, die sich in der Theorie als besonders elegant erweist, nicht praktikabel ist.

Der Begri der Klasse ist nicht eindeutig und repr ¨asentiert unterschiedliche Kon- zepte. Klassen k ¨onnen mathematisch als Mengen interpretiert werden. Zugleich dienen sie der Beschreibung ihrer Instanzen und bieten außerdem Mechanismen zur Erzeugung dieser konkreten Vertreter.

In den achtziger Jahren befassten sich Wissenschaftler damit Alternativen zur klas- senbasierten Programmierung zu erforschen und fokussierten sich dabei unter anderem auf die Verwendung von Prototypen. Je nach Forschungsschwerpunkt variiert die Defi- nition des Begri s in den unterschiedlichen Ver ¨o entlichungen. Anlehnend an [7, S. 92] wird in dieser Arbeit ein Prototyp als ein bereits vorgefertigtes, strukturiertes Objekt mit Eigenschaften und einem Verhalten aufgefasst, das bei Bedarf als Vorlage fur¨ die Erstellung eines neuen Objektes herangezogen werden kann. Ein anschauliches Beispiel f ur¨ diese Definition bietet Lieberman in [8]: Nehmen wir an, wir kennen einen ganz bestimmten Elefanten namens Clyde (weil wir z.B. Tierpfle- ger im ¨ortlichen Zoo sind), dann k ¨onnen wir unser Wissen uber¨ Clyde dazu nutzen, Aussagen uber¨ einen anderen Elefanten, namentlich Fred, zu tre en. Wir k ¨onnen bei- spielsweise sagen, dass Fred grau ist und vier Beine hat. 1 Die Vorteile, die sich aus der Verwendung von Prototypen statt Klassen ergeben, lauten nach [9]:

Es handelt sich um einen naturlicheren¨ Ansatz, da man bei der L ¨osung von Problemen h ¨aug versucht, von einem konkreten Fall ausgehend eine abstrakte L ¨osung zu finden. Die Nutzung von Prototypen spiegelt diese Vorgehensweise auf der Ebene der Programmierung wider.

¨

Eine flexiblere Nutzung von Objekten wird erm ¨oglicht, da Anderungen an ein-

zelnen Objekten nicht alle anderen Instanzen dieses Typs betre en.

Klassen werden oft als komplex und einschr¨ankend empfunden. Beispielsweise werden in der Programmierung von grafischen Oberfl ¨achen h ¨aug gleichartige Elemente ben ¨otigt, die sich nur marginal (z.B. in der Eventverarbeitung) von- einander unterscheiden. Trotz der geringen Di erenzen m usste¨ f ur¨ jedes dieser Elemente gleich eine neue Klasse definiert werden.

1 Die Tatsache, das Fred nicht grau ist oder uber¨

eine unnat urliche¨

Anzahl an Beinen verf ugen¨

k ¨onnte,

Seminararbeit zum Kurs 1908 - Thema: JavaScript

3

2.3 Eigenschaften protoypenbasierter Programmiersprachen

Bevor im n ¨achsten Kapitel mit JavaScript eine Implementierung einer prototypenba- sierten Sprache untersucht wird, sollen in diesem der Teil Arbeit zun ¨achst typische Ei- genschaften einer solchen Sprache vorgestellt werden. Dabei ist zu beachten, dass nicht jede Sprachimplementierung zwingend uber¨ alle vorgestellten Merkmale verfugen¨ muss. Die erste prototypenbasierte Programmiersprache uberhaupt¨ war Self, die 1986 von David Unger und Randall B. Smith entwickelt wurde, und die nachfolgend erl ¨auterten Spracheigenschaften allesamt implementierte [10].

2.3.1 Fehlen von Klassen und Konstruktoren

Charakteristisch fur¨ die prototypenbasierte Programmierung ist das Fehlen von Klas- sen, das allerdings nicht automatisch den Wegfall aller klassenbasierter Konzepte, wie z.B. der Vererbung, zur Folge hat. Durch den hohen Grad an Flexibilit ¨at der proto- typenbasierten Sprachen k ¨onnen diese Mechanismen h ¨aug simuliert werden. In Ab- schnitt 3.5 werden beispielsweise gleich mehrere Varianten zur Realisierung von Verer- bung in der Sprache JavaScript vorgestellt. Mit dem Begri der Klasse f¨allt auch die M ¨oglichkeit der Erzeugung von Instanzen durch Konstruktoren weg. Es stellt sich nun die Frage, wie konkrete Objekte stattdes- sen erzeugt werden k ¨onnen? Hierzu benennt die Literatur verschiedene M ¨oglichkeiten [10, 7, S. 84 .]:

Klonung und Erweiterung existierender Objekte: Bei der Erzeugung eines neuen Objektes durch Klonung wird ein bereits existierendes Objekt als Vorlage f ur¨ ein neues Objekt herangezogen. Es wird neben dem Verhalten auch der Zu- stand des Ursprungsobjektes ( parent ) auf den Klon ( child ) ubertragen.¨ Eine Spezialform des Klonens stellt die Erweiterung dar. Sie erm¨oglicht es, ein konkretes Objekt durch die Benennung eines Ursprungsobjektes und einer Menge von weiteren Eigenschaften und Unterschieden zu erstellen.

Erzeugung ex nihilo: Bevor Objekte geklont oder erweitert werden k ¨onnen, muss allerdings mindestens ein konkretes Objekt existieren. Dies kann bereits durch die Laufzeitumgebung der Sprache bereitgestellt oder ex nihilo vom Pro- grammierer erstellt werden. Unter Letzterem versteht man typischerweise eine Beschreibung eines Objektes durch Benennung und Belegung seiner Eigenschaf- ten und seines Verhaltens an Ort und Stelle der Verwendung.

2.3.2

Slots und Parent-Slot

Objekte werden in prototypenbasierten Sprachen als eine Menge von sog. Slots auf- gefasst. 2 Ein Slot meint dabei ein einfaches Schlussel-Wert-Paar¨ ( key-value-pair ), ent- spricht also der Datenstruktur der Map. Der Zugri auf einen Wert, der sowohl Daten (Zustand des Objekts) als auch Funktionen (Verhalten des Objekts) beinhalten kann, erfolgt uber¨ einen Schl ussel¨ [7, S. 96 f.]. In einigen Sprachen verfugen¨ Objekte uber¨ einen speziellen Slot, den sog. Parent-Slot .

2 Der Begri des Slots stammt aus dem Bereich der k unstlichen¨

Intelligenz und bezeichnet einfach

eine Menge von Daten, die zu einer bestimmten Situation ( frame ) geh ¨oren. Siehe [11] f ur¨ weitere

Informationen.

Seminararbeit zum Kurs 1908 - Thema: JavaScript

4

Dieser Slot beihnaltet eine Referenz auf den Prototypen des Objektes, also das Objekt, das beim Klonen also Vorlage diente. Der Parent-Slot spielt eine entscheidende Rolle

diente. Der Parent-Slot spielt eine entscheidende Rolle Abbildung 1: Schematische Darstellung eines Objektes mit

Abbildung 1: Schematische Darstellung eines Objektes mit n+1 Slots, Quelle: Eigene Darstellung

bei der Realiserung der sog. Delegation , die im n ¨achsten Abschnitt theoretisch erl ¨autert und in Abschnitt 3.4 an praktischen Beispielen in der Programmiersprache JavaScript demonstriert wird [10].

2.3.3 Delegation

In der klassenbasierten Programmierung entsprechen Klassen Baupl ¨anen f ur¨ Objekte. Eine Klasse definiert dabei eine Menge von Attributen, uber¨ die jede ihrer Instanzen

verf ugt.¨ Die Belegung dieser Attribute mit konkreten Werten geschieht individuell auf der Ebene der Instanzen d.h. jede Instanz verf ugt¨ uber¨ ihren eigenen Zustand. 3

Klassen definieren dar uber¨ hinaus das Verhalten ihrer zukunftigen¨

derungen am Verhalten der Klasse haben zur Folge, dass sich das Verhalten aller kon- kreten Objekte dieser Klasse ¨andert. M ¨ochte man ein Objekt erzeugen, dass sich durch zus ¨atzliche Attribute oder ein besonderes Verhalten auszeichnet, so muss eine Speziali- sierung in Form einer Subklasse erzeugt werden, die alle Eigenschaften der Basisklasse erbt und um zus¨atzliche Eigenschaften erweitert wird [8, 7, S. 94 .]. Eine Alternative zur klassenbasierten Vererbung bieten prototypenbasierte Sprachen in Form der sog. Delegation . Erh ¨alt ein Objekt in einer Programmiersprache, die De- legation unterst utzt,¨ eine Nachricht wie z.B. den Abruf einer Eigenschaft, so versucht es zun ¨achst selbst diese Nachricht zu beantworten. Ist dies nicht m ¨oglich, so wird die Nachricht an das Objekt im Parent-Slot des empfangenden Objektes weitergeleitet. Dieser Vorgang kann nun solange wiederholt werden bis die Eigenschaft in einem der Delegaten gefunden wird, oder das Ende der Kette erreicht wird. M ¨ochte nun ein Kind- Objekt einen Attributwert ¨andern, der von seinem Prototypen-Objekt stammt, reicht es aus diesen Wert lokal im Kind-Objekt zu setzen. Dieser Sachverhalt soll noch einmal in Abbildung 2 verdeutlicht werden. Clyde ist der Prototyp von Fred, da Freds Parent-Slot uber¨ eine Referenz auf das Objekt von Clyde verf ugt.¨ Wird nun Fred nach der Anzahl seiner Stoßz¨ahne gefragt, so wird die Anfrage implizit an Clyde delegiert, der als Prototyp von Fred mit 2“ antwortet. Wird Fred wiederum nach seinem Namen gefragt, muss die Anfrage nicht weitergeleitet werden, da er selbst uber¨ einen Slot mit entsprechendem Schl ussel¨ verf ugt.¨

Instanzen. An-

¨

3 Mit Hilfe von statischen Variablen kann in den meisten klassenbasierten Sprachen auch ein gemein- samer Zustand erzeugt werden.

Seminararbeit zum Kurs 1908 - Thema: JavaScript

5

Seminararbeit zum Kurs 1908 - Thema: JavaScript 5 Abbildung 2: Schematische Darstellung der Delegation, Quelle: Eigene

Abbildung 2: Schematische Darstellung der Delegation, Quelle: Eigene Darstellung

In Abschnitt 3.4 wird gezeigt, wie der Mechanismus der Delegation in JavaScript ge- nutzt werden kann, um Vererbung zu simulieren. Der umgekehrte Fall ist allerdings nicht m ¨oglich. Dies liegt an den unterschiedlichen Arten, wie in den prototypen- und klassenbasierten Sprachen mit der Selbstreferenz self bzw. this umgegangen wird. In klassenbasierten Sprachen bezieht sich die Variable self immer auf den Empf ¨anger

die Variable self immer auf den Empf ¨ a n g e r Abbildung 3: Forwarding

Abbildung 3: Forwarding vs. Delegation, Quelle: http://bit.ly/1yGzr2y

eines Methodenaufrufes. Man nennt diese Art von Aufruf auch Forwarding . 4 Es ist also nicht m ¨oglich Methoden so aufzurufen, dass self ein anderes Objekt referenziert. Die Delegation der prototypenbasierten Sprachen erm ¨oglicht dies hingegen, da die Variable self in diesem Fall nicht den Aufgerufenen ( Client ), sondern den Aufrufer ( Server ) referenziert. Neben dem Zustand kann so auch das Verhalten des Prototypen wieder- verwendet werden [8].

2.3.4 Vielfalt prototypenbasierter Programmiersprachen

Am Ende dieses Kapitels soll kurz anhand der beiden Sprachen Kevo und Omega verdeutlicht werden, dass die vorgestellten Eigenschaften nicht notwendigerweise f ur¨ alle Implementierungen prototypenbasierter Sprachen gelten.

Kevo : Die Programmiersprache Kevo verf ugt¨ weder uber¨ einen Parent-Slot noch den Mechanismus der Delegation. Wird ein Objekt geklont, so wird eine un-

abh ¨angige Kopie, die in keinerlei Beziehung zum Parent steht, erzeugt. Es steht

¨

dem Entwickler danach frei beliebige Anderungen an dem neu erzeugten Objekt durchzuf uhren.¨ Objekte k ¨onnen zudem in Gruppen zusammengefasst werden, die

ebenfalls durch sog. modifications ver ¨andert werden k ¨onnen [7, S. 100 .].

Omega : Beim Entwurf der Sprache Omega wurde Wert auf Typsicherheit ge-

in

legt. Man entschied sich daher f ur¨ die Nutzung einer statischen Typpr ufung¨

4 In der klassenbasierten Programmierung werden die Begri e Delegation und Forwarding h ¨aug synonym verwendet. Gemeint ist meist Letzeres, siehe [12, S. 116 f.].

Seminararbeit zum Kurs 1908 - Thema: JavaScript

6

Kombination mit klassischer Vererbung. Objekte k¨onnen in einem Objektbrow- ser erzeugt und von bereits existierenden Objekten abgeleitet werden. Prototypen dienen in Omega daher zus¨atzlich der Definition von Interfaces, da abgeleitete Objekte sicherstellen m ussen,¨ dass das Interface des Prototypen eingehalten wird [13, 7, S. 104].

3 Prototypen in JavaScript

In diesem Kapitel wird die konkrete Implementierung einer prototypenbasierten Pro- grammiersprache anhand der weit verbreiteten Sprache JavaScript vorgestellt. Im ers- ten Teil wird die Geschichte JavaScripts kurz vorgestellt. Nach einem kurzen Exkurs zum Objektbegri besch¨aftigt sich der Hauptteil dieses Kapitels mit dem Konzept des Prototypen, sowie der Delegation und Vererbung.

3.1 Geschichte JavaScripts

Die Programmiersprache JavaScript wurde im Jahr 1995 von Brendan Eich, einem damaligen Mitarbeiter des Unternehmens Netscape Communications, erfunden und umgesetzt. Eine Besonderheit dieser Sprache ist die Tatsache, dass Eich sie in in weni- gen Tagen entwickelt hat, und sie dennoch bis heute weitergenutzt wird [14]. Eich bediente sich dabei an Sprachkonzepten, die er fur¨ besonders n utzlich¨ empfand. Anders als der Name der Sprache vermuten l ¨asst, wurde er nicht durch die Sprache Java, sondern durch die prototypenbasierte Sprache Self und die funktionalen Sprachen Scheme bzw. Lisp inspiriert. Seine Implementierung fand im selben Jahr Einzug in den Netscape Navigator, 5 und wurde bereits ein Jahr sp ¨ater von Microsoft kopiert und als JScript im Internet Explorer angeboten [15, S. 1, 14].

3.1.1 ECMAScript

Die alternativen Implementierungen der Sprache fuhrten¨ zum sog. Browserkrieg und der damit verbundenen Problematik der konkurrierenden DHTML-Implementierungen. Da jeder Browser uber¨ eigene Features und Interfaces verf ugte,¨ mussten Entwickler h ¨aug browser-spezifischen Quelltext schreiben. Um den Inkompatibilit ¨aten und der resultierenden Doppelbelastung entgegenzuwirken, meldete der Microsoft Konzern be- reits 1996 eine Spezifikation der Sprache beim European Computer Manufacturers Association (ECMA) an. Rechtsstreitigkeiten f uhrten¨ dazu, dass der verabschiedete Standard weder Java- noch J-, sondern ECMAScript 6 hieß [15, S. 2]. Die relevanten Versionen von ECMAScript sind die Versionen 3 und 5, die 1999 und 2009 verabschiedet wurden, und die Sprache um wichtige Features wie z.B. Regular Expressions und den Strict mode erweitert haben [16]. In diesem Dokument wird der Begri JavaScript synonym zur Implementierung der ECMAScript Version 5.1 verwendet. Sollte eine andere Version des Standards gemeint sein, so wird an der jeweiligen Stelle explizit darauf hingewiesen.

5 Damals noch unter dem Namen LiveScript. 6 Laut Brendan Eich war die Einigung auf diesen Namen noch tragischer als die tr ugerische¨ nung JavaScript, da ECMA nach einer Hautkrankeit klinge.

Bezeich-

Seminararbeit zum Kurs 1908 - Thema: JavaScript

7

3.1.2 JavaScript als missverstandene Sprache

Douglas Crockford, der mit seinem Werk JavaScript - The Good Parts“, maßgeblich an der Verbesserung von JavaScripts Ruf beteiligt war, bezeichnet unter [17] JavaScript als die am meisten missverstandenste Programmiersprache. Am meisten“ bezeichnet dabei nicht nur die Anzahl der Missverst ¨andnisse selbst, sondern auch die große Menge an Entwicklern, die die Sprache missverstehen. Letzeres liegt vor allem an der großen Verbreitung der Sprache, die von vielen modernen Browsern interpretiert werden kann. Die beiden wichtigsten Missverst ¨andnisse lauten:

Namensgebung : Der Name der Programmiersprache l ¨asst auf eine Verwandt- schaft mit der objektorientierten und vor allem klassenbasierten Sprache Ja- va schließen. Leider haben die beiden Sprachen außer der C-¨ahnlichen Syntax keine Gemeinsamkeiten. Die Bezeichnung JavaScript ist ein reiner Marketing- Schachzug gewesen, der die Sprache popul ¨arer machen sollte [14]. Die Tatsache, dass der Sprachstandard ECMAScript heißt und es diverse Implementierungen gibt (JavaScript, ActionScript, JScript) tat ihr Restliches, um zur allgemeinen Verwirrung beizutragen.

Unbekannte Konzepte : Das Konzept der funktionalen Programmierung ist vielen Entwicklern einfach nicht vertraut. Hinzu kommt, dass die Prinzipien der objektorientierten Programmierung zwar durchaus bekannt sind, aber die meis- ten Sprachen doch auf klassenbasierte Implementierungen setzen. Es ist also nicht verwunderlich, wenn viele Entwickler schon fast krampfhaft versuchen JavaScript die Idee der Klassen aufzudr ucken.¨ Die irrefuhrende¨ Terminologie des sog. Kon- struktors (siehe Abschnitt 3.2.2) ist nicht minder unbeteiligt am Durcheinander [18, S. 90-94].

3.1.3

JavaScript heute

Heute stellt der Browser weiterhin die Hauptdom ¨ane JavaScripts dar, dabei wird die Sprache durch viele Bibliotheken erg ¨anzt, die dem Webdesigner bei seiner t ¨aglichen Ar- beit behilflich sind, und die Di erenzen der verschiedenen Browser-Implementierungen nivellieren. Mit jQuery wird in Kaptitel 4 eine solche Bibliothek vorgestellt. Im Jahr 2009 hat JavaScript den Sprung aus dem Browser hinaus gescha t und kann seit dem universell d.h. auf Client- und Serverseite verwendet werden. Mit node.js schuf der Entwickler Ryan Dahl ein Framework, das sich durch hohe Performanz außerhalb des Browsers auszeichnet. Mit ECMAScript Version 6 befindet sich die n ¨achste Novelle des Sprachstandards be- reits in der Entwicklung. 7

3.2 Objekte in JavaScript

JavaScript verf ugt¨ neben den primitiven Datentypen Number , String und Boolean , den besonderen Typen undefined und null uber¨ Objekte. Zwei besondere Objekte, die durch die verschiedenen Laufzeitumgebungen bereitgestellt werden, sind Funkti- onsobjekte ( Function ) und Felder ( Array ) [15, S. 29]. Der Aufbau eines Objektes

7 Stand November 2014

Seminararbeit zum Kurs 1908 - Thema: JavaScript

8

orientiert sich dabei an dem bereits beschrieben Konzept der Slots, die in JavaScript Properties und im weiteren Verlauf der Arbeit einfach Eigenschaften oder Attribute genannt werden. Ein solches Attribut besteht dabei aus einem Schl ussel-Wert-Paar,¨ wobei der Schl ussel¨ stets vom Typ String sein muss, w¨ahrend der Wert einen beliebi- gen Datentypen annehmen kann [19, S. 48, 15, S. 115]. Da JavaScript frei von Klassen ist, m ussen¨ Objekte auf andere Arten erstellt werden k ¨onnen, dabei unterscheidet die Sprache insgesamt vier unterschiedliche Arten der Ob- jekterzeugung, die in den n¨achsten Unterkapiteln vorgestellt werden sollen.

3.2.1 Objektliterale

Die einfachste Form der Erstellung von Objekten ist ex nihilo in Form sog. Objekt- literale , die eine Erzeugung an Ort und Stelle erm ¨oglichen. Auf diese Art und Weise k ¨onnen Zustand und Verhalten eines Objektes vollst ¨andig definiert werden [20, S. 21]. Die dabei verwendete Notation nennt sich JavaScript Object Notation ( JSON ), und wird im Bereich der Webtechnologie h¨aufig zur Serialisierung von Daten verwendet

[21].

Listing 1: Objekterzeugung mittels Objektliteral

1

var clyde = {

2

name : " Clyde " ,

3

numberOfTusks : 2,

4

makeNoise : function () {

5

console.log ( " Tooeroooe ! " ) ;

6

}

7

}

8

9

console.log ( clyde.name ) ; // " Clyde "

10

console.log ( clyde [ " numberOfTusks " ]); // 2

11

clyde.makeNoise (); // " Tooeroooe !"

In Listing 1 wird ein Objekt namens Clyde“ erstellt, das neben den beiden Eigen- schaften name und numberOfTusks uber¨ die Funktion 8 makeNoise verf ugt.¨ Die Zeilen 9 und 10 des Beispiels zeigen die beiden Arten des Zugri s auf die Eigenschaften ei- nes Objektes. Dabei ist der Zugri mittels sog. Dot-Notation (Zeile 9) ¨aquivalent zum Zugri uber¨ den Schl ussel¨ (Zeile 10), wobei sich Letzterer besonders gut fur¨ reflektive Aufrufe eignet.

3.2.2 Konstruktoren

Neben den Objektliteralen ist eine Objekterzeugung mittels sog. Konstruktoraufruf m ¨oglich. Konstruktoren sind gew ¨ohnliche Funktionen, deren Name per Konvention groß geschrieben wird. Eine Funktion wird allerdings erst als Konstruktor verwendet, wenn sie mit dem Schlusselwort¨ new aufgerufen wird.

8 Man k ¨onnte genau so gut von einer Methode sprechen. Da in JavaScript jede Funktion zu einem Objekt geh ¨ort (im Zweifelsfall zum sog. globalen Objekt) ist jede Funktion gleichzeitig auch ei- ne Methode. Um den funktionalen Charakter der Sprache zu unterstreichen, verwende ich den Ausdruck Funktion.

Seminararbeit zum Kurs 1908 - Thema: JavaScript

9

Listing 2: Objekterzeugung mittels Konstruktoraufruf

1

function Elephant ( name ) {

2

this.name = name;

3

this.numberOfTusks = 2;

4

this.makeNoise = function () {

5

console.log ( " Tooeroooe ! " ) ;

6

}

7

}

8

9

var clyde = new Elephant ( " Clyde " ) ;

10

clyde instanceof Elephant; // true

Analog zum vorherigem Beispiel wird in Listing 1 ein Objekt namens Clyde“ erzugt, allerdings erkennt man am Schl usselwort¨ new in Zeile 9, dass ein Konstruktoraufruf zur Erzeugung verwendet wird. Ein solcher Aufruf hat eine Menge von Operationen zur Folge, die auf den ersten Blick nicht ersichtlich sind und daher an dieser Stelle noch einmal einzeln aufgeschl usselt¨ werden sollen [18, S. 21, 22]:

1. Es wird ein neues (leeres) Objekt erstellt.

2. Die Prototypen-Eigenschaft des Objektes wird gesetzt. Was dies konkret bedeu- tet, und welcher Wert der Eigenschaft zugewiesen wird, wird detailliert in Kapitel 3.3 erl ¨autert.

3. Die Variable this referenziert das neue Objekt und erm ¨oglicht so eine Modifika- tion seiner Eigenschaften.

benennen, so wird

4. Sollte die Konstruktorfunktion keinen explizit R uckgabewert¨ das neue Objekt zur uckgegeben.¨

Das Konzept der Konstruktoren wird h ¨aug kritisiert. Konstruktoren erwecken durch ihren Namen und ihre Syntax den Eindruck JavaScript verf uge¨ uber¨ Klassen. Die Ver- wendung des Schlusselwortes¨ new in Kombination mit Zuweisungen an die Variable this innerhalb der Konstruktorfunktionen erinnern stark an die Verwendung von Kon- struktoren in klassenbasierten Sprachen wie Java oder C#, und f uhren¨ bei unerfahrenen Programmierern und JavaScript-Anf¨angern zu vermeidbaren Verst ¨andnisproblemen. An dieser Stelle sei zudem noch einmal erw ¨ahnt, dass Konstruktoren gew ¨ohnliche Funk- tionen sind, die auch ohne das Schl usselwort¨ new aufgerufen werden k ¨onnen. Leider wird in diesem Fall die Selbstreferenz this an das globale Objekt gebunden, was unter Umst ¨anden zu schwer nachvollziehbaren Fehlern f uhren¨ kann (siehe Listing 3). Die Konsequenz ist, dass einige Autoren von der Verwendung von Konstruktoraufrufen abraten und stattdessen dazu appellieren, die ubrigen¨ Formen der Objekterzeugung zu nutzen, die charakteristisch fur¨ prototypenbasierte Sprachen sind [20, S. 30].

Listing 3: Fehlerhafte Objekterzeugung

1

function Elephant ( name ) {

2

this.name = name;

3

}

4

5

var clyde = Elephant ( " Clyde " ) ; // new fehlt

6

console.log ( clyde.name ) // Fehler

7

console.log ( window.name ) // " Clyde "

Seminararbeit zum Kurs 1908 - Thema: JavaScript

10

3.2.3 Klonung und Erweiterung

In Abschnitt 2.3.1 haben wir bereits die M¨oglichkeit der Objekterzeugung durch Klo- nung bzw. Erweiterung kennengelernt. ECMAScript verf ugt¨ seit Version 5.1 uber¨ die Funktion Object.create , die beide Arten der Objekterzeugung erm ¨oglicht. Da auch Object.create ausgiebig Gebrauch von Prototypen macht, wird an dieser Stelle auf die ausf uhrliche¨ Erl ¨auterung verzichtet und auf die sp ¨atere Betrachtung in Kapitel 3.5.2 verwiesen.

3.3 Prototype-Eigenschaft

Im vorherigen Kapitel wurde bereits mehrfach die sog. Prototyp-Eigenschaft eines Objektes angesprochen. Diese spezielle Eigenschaft referenziert ein weiteres Objekt, n ¨amlich seinen sog. Prototypen. In JavaScript entsprechen Prototypen dem bereits vorgestellte Konzept des Parent-Slots, das bereits am Beispiel von Self erl ¨autert wur- de. Die Bedeutung des Begri s Prototyp“ ist in JavaScript allerdings mehrfach belegt und soll an dieser Stelle genauer betrachtet werden [23, S. 83, 24].

Prototyp als Referenz auf das Elternobjekt : Der Begri Prototyp kann zum einen, wie bereits erl¨autert, eine Referenz auf das Elternobjekt bezeichnen. Es handelt sich dabei um eine spezielle Eigenschaft eines Objektes, die durch den ECMAScript Standard spezifiziert wird. Diese Eigenschaft wird anlehnend an die Notation des Standards auch h ¨aug als [[Prototype]] bezeichnet. In ECMAScript 5.1 und fr uher¨ bezeichnete [[Prototype]] eine interne Eigen- schaft eines Objekts, fur¨ die keine Zugri smethoden existierten. Einige Brow- serhersteller setzen sich in der Vergangenheit uber¨ den Standard hinweg und erm ¨oglichten den Zugri auf das Elternobjekt uber¨ die propriet¨are Eigenschaft proto . Diese Idee wurde im vorl ¨augen ECMAScript 6 Standard aufgegrif- fen und in Form der Eigenschaft Object.prototype. proto ubernommen¨ [25, Kap. B.2.2.1].

Prototyp als Konstruktoreigenschaft: Funktionen verfugen¨ uber¨ eine zu- s ¨atzliche Eigenschaft namens prototype , auf die explizit zugegri en werden kann. Diese Eigenschaft spielt eine wichtige Rolle bei der Objekterzeugung, falls die Funktion als Konstruktur verwendet wird. [[Prototype]] und prototype be- zeichnen im Fall von Funktionen also zwei verschiedene Eigenschaften.

3.3.1 Object.prototype

Wird ein Objekt mittels Objektliteral oder new Object() erzeugt, so verweist [[Proto- type]] auf einen besonderen Prototypen, n ¨amlich Object.prototype . Object.proto- type ist ein besonderes Objekt, das von der entsprechenden Laufzeitumgebung be- reitgestellt wird, und eine Menge von Standardfunktionen (z.B. toString() und is- PrototypeOf() ) bereitstellt. Object.prototype ist insofern besonders, da es selbst uber¨ keinen eigenen Prototypen verfugt¨ [26].

Listing 4: Object.prototype als besonderer Prototyp

1

o = {}

2

Object.getPrototypeOf ( o ) === Object.prototype; // true

Seminararbeit zum Kurs 1908 - Thema: JavaScript

11

3

Object.getPrototypeOf ( Object.prototype ) === null; // true

3.3.2 [[Prototype]] und Konstruktoren

In Abschnitt 3.2.2 wurde bereits gezeigt, dass die Objekterzeugung mittels Konstruk-

¨

toraufruf ebenfalls Anderungen am Prototypen eines Objeks durchf uhrt.¨

en Objekt, das durch den Aufruf erstellt wurde, wird als [[Prototype]] der Wert der Prototyp-Eigenschaft ( .prototype ) des Konstruktors zugewiesen. Anders ausge- dr uckt:¨ Die interne Prototypeneigenschaft ( [[Prototype]] ) referenziert bei Objek- ten, die mittels Konstruktoraufruf erzeugt wurden, das selbe Objekt wie die explizite Prototypen-Eigenschaft .prototype der Konstruktor-Funktion. Listing 5 verdeutlicht diesen Sachverhalt an einem praktischen Beispiel.

Dem neu-

¨

Listing 5: Anderungen an Prototyp-Objekten

1

function Elephant ( name ) {

2

this.name = name;

3

}

4

5

console.log ( Elephant.prototype ) ; // {}

6

7

Elephant.prototype.numberOfTusks = 2;

8

Elephant.prototype.makeNoise = function () {

9

console.log ( " Tooeroooe ! " ) ;

10

}

11

12

var clyde = new Elephant ( " Clyde " ) ;

13

clyde.numberOfTusks === 2; // true

14

clyde.makeNoise (); // " Tooeroooe !"

15

Object.getPrototypeOf ( clyde ) === Elephant.prototype // true

Es wurde bereits mehrfach erw¨ahnt, dass Konstruktoren in JavaScript einfache Funk- tionen sind. Da Funktionen selbst first-class citizens“, also Objekte sind, verfugen¨ sie selbst auch uber¨ Eigenschaften. Eine dieser Eigenschaften ist, die bereits erw ¨ahnte Eigenschaft prototype , die in Zeile 5 zun ¨achst auf eine leeres Objekt verweist und bei Bedarf auch ge¨andert werden kann. In den Zeilen 7-10 wird eben dieses Prototypen- Objekt modifiziert und erh ¨alt neben einem Attribut eine neue Funktion. Wird darauf- hin ein neues Objekt mittels Konstruktoraufruf erzeugt, so sieht man (in den Zeilen 13 und 14), dass dieses Objekt ebenfalls uber¨ die Eigenschaften des Prototypen verfugt.¨ Abbildung 4 stellt zur Verdeutlichung das resultierende Objektgeflecht noch einmal grafisch dar. 9

3.4 Delegation in JavaScript

Es stellt sich nun die Frage, wie im vorherigen Abschnitt ein Objekt auf die Eigenschaf- ten seines Prototypen zugreifen konnte. Dieser Mechanismus wird in JavaScript mittels

9 In Wirklichkeit handelt es sich bei dieser Grafik, die so oder so in ¨ahnlich in vielen Lehrb uchern¨ und auf vielen Webseiten gefunden werden kann um eine Vereinfachung des Sachverhalts. An dem simplen Aufruf Elephant() sind in Wirklichkeit mehr Objekte beteiligt, da die Funktion selbst auch uber¨ die Eigenschaft [[Prototype]] verf ugt.¨ F ur¨ Details siehe [27].

Seminararbeit zum Kurs 1908 - Thema: JavaScript

12

Seminararbeit zum Kurs 1908 - Thema: JavaScript 12 Abbildung 4: Prototypen in JavaScript, Quelle: Eigene Darstellung

Abbildung 4: Prototypen in JavaScript, Quelle: Eigene Darstellung

Delegation realisiert und h ¨aug auch als Prototype-Chaining bezeichnet. Wird eine Ei- genschaft eines beliebigen Objektes abgefragt, so pr uft¨ das Objekt zun ¨achst lokal, ob es uber¨ diese Eigenschaft verf ugt.¨ Ist dies der Fall, so wird die entsprechende Operation durchgefuhrt.¨ Schl ¨agt der Versuch allerdings fehl, so wird der Zugri an das Objekt weitergeleitet, das mittels [[Prototype]] referenziert wird. Dieser Mechanismus wird so lange fortgesetzt bis Object.prototype erreicht wird. Sollte auch dort die gesuch- te Eigenschaft nicht gefunden werden, so wird abgebrochen, da Object.prototype bekanntlich uber¨ keinen weiteren Prototypen verfugt¨ [19, S. 71].

Listing 6: Delegation in JavaScript

1

function Elephant ( name ) {

2

this.name = name;

3

}

4

5

Elephant.prototype.numberOfTusks = 2;

6

Elephant.prototype.makeNoise = function () {

7

console.log ( this.name + " says: Tooeroooe ! " ) ;

8

}

9

10

var clyde = new Elephant ( " Clyde " ) ;

11

var fred = new Elephant ( " Fred " ) ;

12

13

Elephant.prototype.eat = function () {

14

console.log ( " nom nom nom " ) ;

15

}

16

17

clyde.eat (); // " nom nom nom "

18

19

fred.numberOfTusks = 1; // poor fred ;(

20

clyde.numberOfTusks === 2; // true

21

fred.numberOfTusks === 1; // true

22

23

fred.makeNoise (); // " Fred says: Tooeroooe !";

Wir wollen anhand des Listings 6 das Prinzip der Delegation in JavaScript noch einmal nachvollziehen. Nach der Definition des Konstruktors und der Erweiterung des Proto-

Seminararbeit zum Kurs 1908 - Thema: JavaScript

13

typen werden zwei konkrete Elefanten erzeugt. In den Zeilen 13-15 wird der Prototyp der beiden Elefanten nachtr¨aglich, d.h. nachdem die beiden Objekte bereits erzeugt wurden, um die Funktion eat() erweitert. In Zei- le 17 wird nun die nachtr ¨aglich hinzugef ugte¨ Funktion auf einem der beiden Objekte erfolgreich aufgerufen. Dieser Sachverhalt verdeutlicht, dass es sich bei einem Proto- typen um ein gew ¨ohnliches Objekt handelt, das beliebig modifiziert werden kann. Es zeichnet sich nur als Prototyp aus, weil es durch [[Prototype]] eines beliebigen ande- ren Objektes referenziert wird. Der Mechanismus der Delegation sorgt daf ur,¨ dass die

beiden Objekte clyde und fred indirekt uber¨ den Zustand und das Verhalten ihres Prototypen verfugen.¨ Wir wollen nun den umgekehrten Fall betrachten und eine Eigenschaft eines bereits erzeugten Elefanten modifizieren. In Zeile 19 verliert“ Fred einen Stoßzahn. In den bei- den folgenden Zeilen sieht man, dass die Zuweisung nur Auswirkungen auf das Objekt fred hat. Dies erkl ¨art sich dadurch, dass die Zuweisung die Erzeugung einer neuen Eigenschaft im Objekt selbst zur Folge hat, und nicht an den Prototypen delegiert wird. Das Objekt fred verf ugt¨ nun also uber¨ einen eigenen Wert f ur¨ die Eigenschaft numberOfTusks , was zur Folge hat, dass der Mechanismus des Prototype-Chaining be- reits vorzeitig f undig¨ wird, und die Anfrage nicht an [[Prototype]] delegiert werden muss. Man nennt dieses Verhalten Shadowing und es bietet den Vorteil, dass lokale

¨

Anderungen an einem Kind eines Prototypen nicht ungewollt alle anderen Kinder des

gleichen Prototypen modifizieren. Abbildung 5 stellt die beiden Objekte und die Beziehung zu ihren Prototypen noch einmal grafisch dar.

Beziehung zu ihren Prototypen noch einmal grafisch dar. Abbildung 5: Delegation in JavaScript, Quelle: Eigene

Abbildung 5: Delegation in JavaScript, Quelle: Eigene Darstellung

3.5 Vererbung in JavaScript

Bisher wurde nur die Beziehung zwischen einem Objekt und seinem Prototypen be- trachtet. Dieser einstufige Zusammenhang entspricht im Wesentlichen der Beziehung zwischen einem Objekt und seiner Klasse in der klassenbasierten Programmierung.

Seminararbeit zum Kurs 1908 - Thema: JavaScript

14

Es ist allerdings leicht zu erkennen, dass sich mit dem Ansatz der Delegation eine mehrstufige Anordnung von verschiedenen Objekt erzielen l ¨asst, mit der die klassenba- sierte Vererbung (implementation inheritance ) simuliert werden kann. In der Literatur werden dabei verschiedene Ans ¨atze diskutiert, die an dieser Stelle vorgestellt werden sollen.

3.5.1 Pseudo-klassenbasierte Vererbung

Anhand von Listing 7 wollen wird die sog. pseudo-klassenbasierte Vererbung betrach- ten.

Listing 7: Pseudo-klassenbasierte Vererbung

1

function Mammal ( name ) {

2

this.name = name;

3

}

4

5

Mammal.prototype.eat = function () {

6

console.log ( " nom nom nom " ) ;

7

}

8

9

function Elephant ( name ) {

10

this.name = name;

11

this.numberOfTusks = 2;

12

}

13

14

Elephant.prototype = new Mammal ();

15

Elephant.prototype.makeNoise = function () {

16

console.log ( this.name + " says: Tooeroooe ! " ) ;

17

}

18

19

var clyde = new Elephant ( " Clyde " ) ;

20

clyde.name === " Clyde " ; // true

21

clyde.numberOfTusks === 2; // true

22

clyde.eat (); // " nom nom nom "

23

clyde.makeNoise (); // " Clyde says: Tooeroooe !") ;

Diese Form der Vererbung in JavaScript orientiert sich stark an der klassenbasierten Vererbung. Es werden zwei Konstruktoren definiert, deren Prototypen uber¨ zus ¨atzliche

Eigenschaften verfugen.¨ Die tats¨achliche Implementierung der Vererbung findet in Zei-

¨

le 14 des Listings 7 statt. Durch das Uberschreiben der Prototyp-Eigenschaft des Konstruktors wird ein neues Objekt (ein S¨augetier) erzeugt, dessen interne [[Proto- type]] -Eigenschaft auf Mammal.prototype verweist. Das entstandene Objektgeflecht ist in Abbildung 6 dargestellt. Dieser h ¨aug verwendete Ansatz wird gerne um weiteren Funktionalit¨aten, wie z.B abstrakten Klassen, erweitert (siehe z.B. [15, Kap. 9.7.4]. Es gibt allerdings auch Kritik an dieser Art der Realisierung von Vererbung, die im Wesentlichen mit der Verwendung von Konstruktoraufrufen zusammenh¨angt. Neben

der bereits bekannten Kritik an der Mechanik des Konstruktoraufrufs in JavaScript, ist diese Variante im Entwurf unvorteilhaft. Zum einen wird durch den Aufruf new Mammal() in Zeile 14 ein konkretes S ¨augetier erzeugt, obwohl man ja eigentlich nur einen Prototypen ben¨otigt. 10 Es findet also konzeptionell eine Vermischung zwischen

10 Elephant.prototype = Mammal.prototype; ist ubrigens¨

S ¨augetiere zu Elefanten w urden¨

und umgekehrt.

auch keine Option, da dadurch alle

Seminararbeit zum Kurs 1908 - Thema: JavaScript

15

Seminararbeit zum Kurs 1908 - Thema: JavaScript 15 Abbildung 6: Pseudo-klassenbasierte Vererbung, Quelle: Eigene

Abbildung 6: Pseudo-klassenbasierte Vererbung, Quelle:

Eigene Darstellung

den Objekten, die konkrete Instanzen repr ¨asentieren, und Objekten, die Klassen re- pr ¨asentieren, statt. Auf den ersten Blick k ¨onnte man ebenfalls meinen, es f ¨ande, bedingt durch den Aufruf in Zeile 14, eine Verkettung von Konstruktoraufrufen statt. Dies erweist sich allerdings als falsch, denn wird ein neues Objekt mit new Elephant() erstellt, so wird keineswegs der Konstruktor des S ¨augetiers (Zeile 1) aufgerufen, stattdessen m ussen¨ die geerbten Attribute in der Konstruktorfunktion der Subklasse“ wiederholt werden (Zeile 10). 11 Der Konstruktor des S ¨augetiers wird in Listing 7 lediglich einmal zur Erzeugung des Prototypenobjektes ausgefuhrt.¨ Da zum Zeitpunkt des Aufrufs auch keine sinnvol- len Daten zur Verfugung¨ stehen, werden auch keine Argumente an den Konstruktor ubergeben.¨ Dies kann zus ¨atzlich zu unvorhergesehenen Komplikationen f uhren,¨ wenn der Konstruktor sinnvolle Werte fur¨ seine korrekte Ausfuhrung¨ ben ¨otigt [20, S. 48, 18, S. 102].

3.5.2 Prototypenbasierte Vererbung

Bis zur Freigabe des ECMAScript Standards in der Version 5.1 war die im vorheri- gen Abschnitt vorgestellte Methode die bevorzugte Art und Weise Vererbung in Java- Script zu simulieren. Mit der genannten Version des Standards wurde die Funktion Object.create() eingef uhrt,¨ die einen nat urlicheren¨ Weg zur Realisierung von Verer- bung erm ¨oglicht. Die Funktion erm ¨oglicht eine Vererbung unter Objekten, unabh ¨angig von Klassenobjekten und Konstruktoren, durch Klonung und Erweiterung existierender Objekte. Sie wird wahlweise mit einem oder mit zwei Argumenten aufgerufen, wobei das erste Argument stets das bereits vorhandene Objekt erwartet, das der [[Proto- type]] -Eigenschaft eines neu erzeugten Objektes zugewiesen werden soll [18, S. 109 f.]. Die Funktionsweise soll an Listing 8 nachvollzogen werden.

Listing 8: Object.create Polyfill

1

if ( typeof Object.create ! == ' function ' ) {

2

Object.create = function ( o ) {

11 In Abschnitt 3.5.3 wird gezeigt, wie man zumindest diese Unsch ¨onheit beseitigen kann.

Seminararbeit zum Kurs 1908 - Thema: JavaScript

16

3

 

function F () {}

4

F.prototype = o;

5

return new F ();

6

};

7

}

8

9

newObject = Object.create ( oldObject ) ;

Listing 8 zeigt eine m ¨ogliche Implementierung der Methode Object.create() und gleichzeitig einen sog. Polyfill , einen Quelltext, der dazu dient Browserinkompatibi- lit ¨aten zu schließen, indem eine propriet ¨are Funktionalit ¨at mit bereits standardisierten Mitteln nachgestellt wird. In diesem konkreten Fall handelt es sich um eine Imple- mentierung, die eine eingeschr ¨ankte Version von Object.create() bereitstellt [23, S. 91, 20, S. 22]. In Zeile 3 wird ein tempor ¨arer Konstruktor erstellt, dessen prototype - Eigenschaft im n ¨achsten Schritt mit dem ubergebenen¨ Objekte o uberschrieben¨ wird. Zeile 5 hat zur Folge, dass ein neues Objekt zur uckgegeben¨ wird, dessen [[Proto- type]] -Eigenschaft auf das ubergebene¨ Objekt o verweist. 12 Die standardisierte Implementierung der Funktion bietet zus ¨atzlich die M ¨oglichkeit ei- ne Menge von Eigenschaften, in Form eines weiteren Parameters, zu ubergeben,¨ um die das neue Objekt erweitert wird.

Listing 9: Klonung und Erweiterung von Objekten

1

var clyde = { name : " Clyde " , numberOfTusks :2}

2

3

// " cloning "

4

var fred = Object.create ( clyde ) ;

5

fred.name = " Fred " ;

6

fred.numberOfTusks === 2; // true

7

fred.african = true;

8

9

// extending

10

var bonnie = Object.create ( fred , {

11

isMatriarch: {

12

configurable: true ,

13

enumerable: true ,

14

value: true ,

15

writable: true

16

} ,

17

name: {

18

configurable: true ,

19

enumerable: true ,

20

value: " Bonny " ,

21

writable: true

22

}

23

});

24

25

bonnie.name === " Bonny " ; // true

26

bonnie.isMatriarch === true; // true

Im Listing 9 wird Object.create() verwendet, um eine mehrstufige Beziehung zwi- schen Objekten zu realisieren. Die Zeile 4 zeigt die Verwendung der Funktion zur

12 Laufzeitumgebeungen, die ECMAScript 5.1 unterst utzen,¨ gen dieser Funktion an.

bieten nat urlich¨

native Implementierun-

Seminararbeit zum Kurs 1908 - Thema: JavaScript

17

Klonung eines existierenden Objektes. In den Zeile 10-23 erfolgt eine Erweiterung, des zuvor erstellen Klons, um die Eigenschaft isMatriarch . Die Erl ¨auterung der Notation zur Erweiterung eines Objektes (configurable , enumerable usw.) w urde¨ an dieser Stelle den Rahmen der Arbeit sprengen. Es ist lediglich darauf hinzuweisen, dass die Eigenschaft value den tats¨achlichen Wert, den die neu hinzugef ugte¨ Eigenschaft er- halten soll, enth ¨alt. Zus ¨atzlich wird der Wert des bereits vorhandenen Attributs name in den Zeilen 17-21 uberschrieben.¨ Diese Form der Vererbung wird auch h ¨aug di erentielle Vererbung genannt und er- laubt in JavaScript sogar das Entfernen vorhandener Attribute. Das resultierende Ob- jektgeflecht wird in Abbildung 7 gezeigt.

resultierende Ob- jektgeflecht wird in Abbildung 7 gezeigt. Abbildung 7: Prototypenbasierte Vererbung, Quelle: Eigene

Abbildung 7: Prototypenbasierte Vererbung, Quelle: Eigene Darstellung

3.5.3 Mischformen

In der jungeren¨ Literatur zu JavaScript finden sich zunehmend Mischformen aus den beiden vorgestellten Vererbungsmechanismen. So k ¨onnen beispielsweise die Nachtei- le der pseudo-klassenbasierten Vererbung durch den Quelltext aus Listing 10 abge- schw¨acht werden [28, 19, S. 69-72, 18, S. 100-103].

Listing 10: Prototypenbasierte Vererbung

1

function Mammal ( name ) {

2

this.name = name;

3

}

4

5

function Elephant ( name ) {

6

Mammal.call ( this , name ) ;

7

this.numberOfTusks = 2;

8

}

9

10

Elephant.prototype = Object.create ( Mammal.prototype ) ;

11

Elephant.prototype.makeNoise = function () {

12

console.log ( this.name + " says: Tooeroooe ! " ) ;

13

}

14

Seminararbeit zum Kurs 1908 - Thema: JavaScript

18

15 var clyde = new Elephant ( " Clyde " ) ;

18 15 var clyde = new Elephant ( " Clyde " ) ; 16 clyde.name ===

16 clyde.name === " Clyde " ; // true

17 clyde.numberOfTusks === 2; // true

18 clyde.makeNoise (); // " Clyde says: Tooeroooe !") ;

Die beiden wesentlichen Unterschiede zur pseudo-klassenbasierten Version werden in Zeile 10 ersichtlich, da dort der Konstruktoraufruf von Mammal durch einen Aufruf von Object.create(Mammal.prototype) ersetzt wurde. Dies bringt den Vorteil, dass die bereits erw ¨ahnte Vermischung zwischen Instanzobjekten und Klassenobjekten aufgel ¨ost wird, so dass zwei sauber voneinander getrennte Hierarchien gepflegt werden k ¨onnen. Zudem erfolgt kein unn¨otiger Aufruf des Konstruktors Mammal , der zu unvorherseh- baren Programmabsturzen¨ oder Nebene ekte f uhren¨ kann. An dieser Stelle sei noch auf die Zeile 6 hingewiesen, die auch so im Beispiel der pseudo-klassenbasierten Ver- sion h ¨atte auftauchen k ¨onnen. Es handelt sich dabei um einen indirekten Aufruf des Konstruktors Mammal , bei dem der Inhalt des ersten Arguments an die lokale Variable this innerhalb des Konstruktors gebunden wird. In diesem Fall heißt das ubergebene¨ Argument ebenfalls this , da es ja das Objekt repr ¨asentiert, das durch den Aufruf new Elephant() erzeugt wurde, und entsprechend initialisiert werden soll.

3.5.4 Mixins als Alternative

Der Vollst ¨andigkeit halber soll hier noch eine Alternative zur Vererbung gezeigt werden, die ohne Prototypen arbeitet. Diese Alternative stellen die sog. Mixins dar, die kurz am Quelltextbeispiel 11 nachvollzogen werden sollen [19, S. 84 f.].

Listing 11: Beispiel fur¨ die Implementierung eines Mixins

1

function mixin ( receiver , supplier ) {

2

for ( var property in supplier ) {

3

if ( supplier.hasOwnProperty ( property )) {

4

receiver [ property ] = supplier [ property ]

5

}

6

}

7

return receiver;

8

}

Das Schl usselwort¨ in in der zweiten Zeile von Listing 11 erm ¨oglicht den iterativen Zugri auf alle Eigenschaften eines Objektes. Mit hasOwnProperty() kann gepr uft¨ werden, ob die Eigenschaft eines Objektes von diesem Objekt selbst stammt oder mit- tels Delegation von einem Prototypen abgeleitet wurde. Handelt es sich um eine eigene Eigenschaft, so wird diese an den Empf¨anger ( receiver ) weitergegeben. Nach Been- den der Schleife verf ugt¨ der Empf ¨anger uber¨ alle Eigenschaften des Vorlage-Objektes ( supplier ). Mit dieser Methode w ¨are es m ¨oglich mehrere Objekte miteinander zu ver- mischen und so eine Art von Mehrfachvererbung zu implementieren, wobei allerdings schnell klar werden sollte, dass man sp¨atestens bei Eigenschaften mit gleichem Namen vor einem Problem st unde.¨

4 JavaScript in der Praxis

Nachdem im vorherigen Kapitel JavaScript und dessen Prototypen vorgestellt wurden, befasst sich dieser Teil der Arbeit mit der professionellen Nutzung von JavaScript in

Seminararbeit zum Kurs 1908 - Thema: JavaScript

19

der Praxis. Der Schwerpunkt liegt hierbei auf der Untersuchung der weit verbreiteten Bibliothek jQuery. Nach einer kurzen Vorstellung der Software und ihres Funktions- umfangs, erfolgt im letzten Teil des Kapitels eine genauere Betrachtung von relevanten Quelltextausschnitten.

4.1 Das jQuery-Projekt

Wichtigstes Merkmal der Implementierung von JavaScript in Browsern ist die sog. Do- cument Object Model-API , die bereits 1998 durch das World Wide Web Consortium (W3C) standardisiert wurde und einen einheitlichen Zugri auf die Elemente eines HTML-Dokumentes erm ¨oglicht. Diese Schnittstelle benennt diverse Funktionen, mit denen die hierarchisch angeordneten Elemente einer XML-Struktur modifiziert und durchlaufen werden k ¨onnen [29]. In der Vergangenheit implementierten Browserhersteller diese Schnittstelle in ihren Produkten und erweiterten sie h ¨aug um eigene Funktionen, die mit der Zeit h ¨aug selbst in den Standard gelangten. Leider wiesen viele Browser Eigenheiten bei der Im- plementierung auf, die von den Nutzern h ¨aug durch Zwischenl ¨osungen (z.B. der bereits vorgestellte Polyfill) kompensiert werden mussten. Die Folge war, dass im Laufe der Zeit Bibliotheken entstanden, die diese einzelnen Browserinkompatibilit¨aten kapselten und h ¨aug uber¨ weitere, eigene Funktionen verf ugten.¨ Eine dieser Erweiterungen, die von vielen Entwicklern begrußt¨ wurde, war es CSS- Selektoren zur Auswahl von Elementen des DOMs zu nutzen, und so die unflexiblen Funktionen des W3C-Standards zu verbessern. Cascading Stylesheets boten bereits in der Version 1 im Jahr 1996 eine vielseitige M¨oglichkeiten auf die einzelnen Elemen- te eine HTML/XML-Dokuments zuzugreifen. W¨ahrend der Standard des W3C nur einen Zugri auf DOM-Elemente uber¨ ihren Typ oder das Attribut ID vorsieht, un- terst utzten¨ CSS-Selektoren weitere Zugri sm ¨oglichkeiten uber¨ Klassen, Pseudoklassen und Pseudoelemente [30]. Dar uber¨ hinaus ist die die Verwendung der Selektoren vielen Entwicklern gut bekannt. Der Entwickler John Resig war ebenfalls von dieser Idee uberzeugt,¨ bem ¨angelte al- lerdings ihre Ausfuhrung¨ in Form der damals verwendeten Bibliotheken (z.B. beha- viour.js). Resig entwicklelte daraufhin seine eigene Bibliothek namens jQuery, die im August 2005 ver ¨o entlich wurde und bis heute aktiv gepflegt und genutzt wird. Kern- bestandteil der Bibliothek war der von ihm entwickelte Code zur Nutzung von CSS- Selektoren in JavaScript, um einzelne Element oder Gruppen von Elementen selektieren zu k ¨onnen [31]. 13 Wie e zient diese Mechanik im Vergleich zu herk ¨ommlichen Java- Script ist, zeigt Listing 12.

Listing 12: Vergleich JavaScript vs. jQuery

1

// jQuery

2

$ .post ( ' www.example.com / login ' , { username: ' fred ' } , function ( data ) {

3

// Verarbeitung

4

})

5

6

// JavaScript

13 Dieser Kern heißt heute Sizzle.js und wird als selbstst ¨andiges Projekt unter http://sizzlejs.com/ weitergepflegt.

Seminararbeit zum Kurs 1908 - Thema: JavaScript

20

7

var httpRequest = new XMLHttpRequest ()

8

httpRequest.onreadystatechange = function ( data ) {

9

// Verarbeitung

10

}

11

httpRequest.setRequestHeader ( ' Content-Type ' , ' application / x-www-form-urlencoded ' )

12

httpRequest.open ( ' POST ' , ' www.example.com / login ' )

13

httpRequest.send ( ' username= ' + encodeURIComponent ( ' fred ' ) )

Listing 12 zeigt einen asynchronen Serveraufruf mittels der POST-Methode des HTTP- Protokolls. Die Zeilen 2-4 zeigen die Implementierung mittels jQuery, die ubrigen¨ Zeilen die klassische Variante mit JavaScript. Der reduzierte Aufwand fur¨ den Programmierer wird durch den Umfang der beiden Beispiele deutlich.

JQuery hat sich im Laufe der Jahre zu einem De-facto-Standard entwickelt. Viele Quelltext-Ausschnitte in Fachb uchern¨ und auf Internetportalen nutzen die jQuery- Notation ohne explizit darauf hinzuweisen. Deutlich wird die hohe Verbreitung, wenn man sich in Abbildung 8 die Nutzung und Marktanteile der Bibliothek im Vergleich zur Konkurrenz anschaut. Von den beobachteten Internetseiten, die http://www.w3techs. com in regelm ¨aßigen Abst ¨anden untersucht, nutzen 35,1% uberhaupt¨ keine Bibliothe- ken. 61,3% der beobachteten Seiten nutzen jQuery, was einem Marktanteil von 93,4% entspricht. 14 Die Webseite http://www.builtwith.com benennt f ur¨ den November 2014 49,4 Mio. Internetauftritte, die Verwendung von der Software machen. Abgeschlagen auf dem zweiten Platz befindet sich der Konkurrent MooTools mit 3,6 Mio. Installationen. Die- sen starken Erfolg verdankt jQuery nicht nur der Selektor-Mechanik, sondern auch einer Menge von weiteren Funktionen, um die das Projekt im Laufe der Jahre erwei- tert wurde [32, 33]. Die wichtigsten Funktionen lauten:

DOM-Manipulation: Der wichtigste Einsatzzweck ist die bereits beschriebe- ne Ver ¨anderung des Document Object Model innerhalb eines Webbrowsers. Be- reits existierende DOM-Elemente k ¨onnen mittels CSS-Selektoren ausgew ¨ahlt und ver ¨andert werden. Neue Elemente k ¨onnen bequem erzeugt und in die existierende Hierarchie eingef ugt¨ werden.

Ereignisbehandlung : Als clientseitige Technologie nutzt JavaScript ausgiebig Techniken der Ereignisbehandlung. Die jQuery-Bibliothek unterst utzt¨ den Ent- wickler mit einer Vielzahl von browserunabh ¨angigen Ereignisfunktionen, die auf DOM-Elemente angewendet werden k ¨onnen. Dabei bietet jQuery sogar die M ¨og- lichkeit auf Ereignisse zuk unftiger¨ Elemente, also Teilen des Dokuments, die nach- tr ¨aglich dynamisch hinzugef ugt¨ wurden, zu reagieren.

¨

Animationen und E ekte : Um Nutzer auf Anderungen innerhalb des Doku- mentes aufmerksam zu machen und ggf. uber¨ Erfolg und Misserfolg einer Aktion

zu informieren, wird eine Menge von Animationen und E ekten angeboten. 15

AJAX : AJAX steht fur¨ Asynchronous JavaScript and XML und beschreibt ei- ne Technik basierend auf dem XMLHttpRequest-Objekt , einer Funktionalit¨at

14 Es ist zu beachten, dass Webseiten auch h ¨aug mehrere Bibliotheken gleichzeitig nutzen. 15 Das Projekt jQueryUI auf http://jqueryui.com/ erweitert diese Funktionalit ¨at noch einmal deutlich.

Seminararbeit zum Kurs 1908 - Thema: JavaScript

21

Seminararbeit zum Kurs 1908 - Thema: JavaScript 21 Abbildung 8: Verbreitung verschiedener JavaScript Bibliotheken

Abbildung 8: Verbreitung verschiedener JavaScript Bibliotheken [Stand 15.12.2014], Quelle: w3techs.com

des Browsers, die asynchrone HTTP-Anfragen an einen Server erm ¨oglicht. Mo- derne Webanwendungen, die in Aufbau und Funktion an klassische Desktop- Applikationen erinnern (sog. Single Page Applications ), w ¨aren ohne AJAX nicht m ¨oglich. Detaillierte Informationen zum Thema AJAX k ¨onnen [34, Kap. 18] ent- nommen werden. JQuery bietet mit seinem ajax -Objekt eine vielseitig nutzbare Schnittstelle zur Verwendung von AJAX innerhalb diverser Laufzeitumgebungen. Ein Beispiel fur¨ einen solchen AJAX-Aufruf wurde bereits mit der Methode post un Zeile 2 von Listing 12 gebracht.

F ur¨ den Entwickler ergeben sich aus der Nutzung von jQuery einige Vorteile [35, S. 105 .]:

Weniger Quelltext : Write less, do more“ lautet das Motto von jQuery. Das gezeigte Quelltextbeispiel (Listing 12) sollte vermittelt haben, dass jQuery dies durchaus gelingt.

Erh ¨ohte Lesbarkeit : Die Tatsache, dass durch geschickte Nutzung von Ereig- nismethoden s ¨amtlicher Quelltext in JavaScript vom eigentlichen XML/HTML getrennt werden kann (sog. unobtrusive JavaScript ), erh ¨oht zusammen mit den kurzen Quelltexten die Lesbarkeit.

Seminararbeit zum Kurs 1908 - Thema: JavaScript

22

Browserkompatibilit¨at : Die Gew ¨ahrleistung der Browserkompatibilit ¨at wur- de nun bereits mehrfach erw ¨ahnt. JQuery unterst utzt¨ sieben g ¨angige Browser, darunter zwei mobile Anwendungen.

Einfach zu erlernen : Durch die einfache Syntax, die Verwendung von CSS-

Selektoren und gut dokumentierten Funktionen, die mit wenig Aufwand viele

¨

Anderungen gleichzeitig am DOM zulassen, ist jQuery leicht zu erlernen.

Große Community : Mit der bereits besprochenen hohen Verbreitung geht auch eine sehr große Entwicklergemeinschaft einher.

Kostenlos : Die Bibliothek ist kostenlos und kann auf der Projektwebseite her- untergeladen werden oder uber¨ ein sog. Content Delivery Network (CDN) in das eigene Projekt integriert werden.

Erweiterbarkeit: JQuery bietet Erweiterungsm ¨oglichkeiten, mit denen das Pro- jekt um zus¨atzliche Funktionen erg ¨anzt werden kann. Wie die Erweiterung der Bibliothek technisch realisiert wird, ist unter anderem Inhalt des n¨achsten Ab- schnitts.

Demgegen uber¨

steht eine geringe Anzahl von Nachteilen:

Dateigr ¨oße: Die Bibliothek ist durch den hohen Funktionsumfang mit einer Dateigr ¨oße von 86 KByte sehr groß. Dies kann vor allem im mobilen Bereich zu langen Ladezeiten f uhren.¨

Verdr¨angung von JavaScript : Die hohe Verbreitung jQueries hat zur Folge, dass viele Entwickler kaum Kontakt mit den standardisierten Browser-APIs ha- ben und f ur¨ viele Einsatzf¨alle nur noch die Bibliotheksfunktionen nutzen. Prinzi- piell spricht nichts gegen ein solche Nutzung, solange der Entwickler uber¨ Kennt- nisse der dahinter liegenden Sprache verf ugt¨ und nicht blind auf fremde Imple- mentierungen vertraut.

4.2 Untersuchung ausgew¨ahlter Quelltextpassagen

Der Erfolg der jQuery-Bibliothek h ¨angt unter anderem stark von seiner einfachen Ver- wendung und der bereits erw ¨ahnten Erweiterbarkeit ab. Diese beiden Erfolgsfaktoren w ¨aren ohne die e ektive Nutzung JavaScripts funktionaler und prototypenbasierter Spracheigenschaften sicherlich nicht m ¨oglich gewesen. An ausgew¨ahlten Teilen des Pro- jektquelltextes 16 soll diese Aussage belegt werden.

4.2.1 Initialisierung des jQuery-Objekts

Kern der Implementierung von jQuery ist das sog. jQuery-Objekt . Es handelt sich dabei um ein JavaScript-Objekt, das alle Bibliotheksmethoden vereint. Bereits das Initialisieren diese Objektes macht ausgiebig Gebrauch von JavaScripts funktionalen Eigenschaften.

Seminararbeit zum Kurs 1908 - Thema: JavaScript

23

Listing 13: Erstmalige Erzeugung des jQuery-Objektes

1

( function ( global , factory ) {

2

// call factory

3

}( typeof window ! == " undefined " ? window : this , function ( window , noGlobal ) {

4

// define factory

5

}

Es handelt sich bei dem Konstrukt in Listing 13 um eine sog. Immediately Invoked Function Expression (IIFE), also um die Definition einer anonymen Funktion, die direkt ausgefuhrt¨ wird. Die Bibliothek stellt so sicher, dass sie an der Stelle, an der sie in ein Projekt eingebunden wird, auch ausgefuhrt¨ wird. Die Parameter der Funktion sind zum einen das globale Objekt, das durch die Laufzeitumgebung bereitgestellt wird, und zum anderen eine anonyme Funktion, die fur¨ die eigentliche Initialisierung des Objektes zust¨andig ist und vom Funktionsparameter als factory bezeichnet wird. Die Erzeugung eines jQuery-Objektes ist sehr simpel und wird in Listing 14 gezeigt.

Listing 14: Erzeugung eines jQuery-Objektes

1

$ ( " a " )

In Listing 14 wird ein Objekt erzeugt, das alle Elemente des Typs a , also alle(!) An- kerpunkte innerhalb des Dokumentes referenziert. Es f ¨allt auf, dass f ur¨ die Objekter-

zeugung weder das Schl usselwort¨ genutzt wird.

new noch eine explizite Benennung eines Prototypen

Listing 15: Initialisierung des jQuery-Objektes

1

// Define a local copy of jQuery

2

jQuery =