Das beste an ECMAScript (good parts) Das beste an JavaScript

Hier wird nicht versucht, ECMAScript vollständig zu beschreiben ( schwache Typisierung von Datentypen, dynamische Objekte, Lambda, ausdrucksstarke literale Objektnotation, funktioninterner Geltungsbereich von Variablen, Closure, automatisches Speichermanagement, usw. ), sondern eher jene ausdruckstarke Teilmenge herausgestellt, die zu zuverlässigen, robusten Programmen führt. In gewisser Weise ist ECMAScript ein "Lisp im C-Gewand".
Historisches Dank an ...

Ein Anwendungsprogramm (kurz Anwendung = Applikation, engl. application software) braucht ein Betriebssystem und eine geeignete Laufzeitumgebung. Etwa so wie abstrakte Wissenschaftstheorien eine Historie und ein geistig-abstraktes Bezugssystem haben, hängen auch Software-Theorien und umfangreiche Projekt-Entwürfe und -Entwicklungen vom konkreten Kontext ab. Zu Entwurfsmustern (Design Patterns) gehören die Kriterien des Zwecks (purpose) und des Bereichs, auf den sie wirken (scope). Es gibt z.B.

Erzeugungsmuster (Creational Pattern) 
a) Klassenmuster: 
   Fabrikmethode (Factory Method, Virtual Constructor)
b) Objektmuster: 
   Abstrakte Fabrik (Abstract Factory, Kit)
Erbauer        (Builder)
Prototyp       (Prototype)
Einzelstück    (Singleton)
Strukturmuster (Structural Pattern) 
a) Klassenmuster:
   Adapter (Adapter, Wrapper) (Adapter mit Vererbung oder Klassenadapter)
b) Objektmuster: 
   Adapter (Adapter, Wrapper) (Adapter mit Assoziation oder Objektadapter)
Brücke         (Bridge, Handle/Body)
Kompositum     (Composite)
Decorator      (Wrapper)
Fassade        (Facade)
Fliegengewicht (Flyweight)
Stellvertreter (Proxy, Surrogate)

Verhaltensmuster (Behavioral Pattern) a) Klassenmuster: Interpreter (Interpreter), Schablonenmethode (Template Method) b) Objektmuster: Zuständigkeitskette (Chain of Responsibility), Kommando (Befehl, Command, Action, Transaction), Iterator (Iterator, Cursor), Vermittler (Mediator), Memento (Memento, Token), Beobachter (Observer, Dependents, Publish-Subscribe, Listener), Zustand (State, Objects for States), Strategie (Strategy, Policy), Besucher (Visitor)

Die Beschreibung eines Entwurfsmusters durch die "Gang of Four" folgt dem folgenden Schema:

Brendan Eich (1964- ) entwickelte LiveScript (Vorläufer von JavaScript). Biografie von Brendan Eich etwa: 7 Jahre bei Firma Silicon Graphics, 3 Jahre bei MicroUnity Systems Engineering, (Betriebssystemkerne, DSP, Portierung des C-Compilers gcc für den MIPS-R4000), ab 1995 Arbeiten an Mocha-LiveScript-JavaScript und Netscape Navigatorbei Netscape Communications, anfang 1998 half Brendan Eich bei der Gründung von mozilla.org, seit 2005 Chief Technical Officer der Mozilla Corporation (siehe z.B. Brendan Eich )

Sprachen haben starke und schwache Ausdrucksformen. Viele Features hat JavaScript von anderen Sprachen übernommen. Die Syntax ist an Java angelehnt, die Funktionen stammen von Scheme und die prototypische Vererbung von Self. Die regulären Ausdrücke wurden von Perl übernommen

Douglas Crockford: "JavaScript kam aus dem Nichts und breitete sich in alarmieren kurzer Zeit weltweit aus. Es gab nie eine Laborphase, in der sie ausprobiert und aufpoliert werden konnte. Sie wurde, so wie sie war, direkt in Netscape Navigator 2 integriert und war zu diesem Zeitpunkt nicht sonderlich ausgereift. Als Java®-Applets versagten, wurde JavaScript zur "Sprache des Web". Die Popularität von JavaScript hat nahezu nichts mit seiner Qualität als Programmiersprache zu tun."
Einfache Datentypen Was bedeutet Typisierung?

Typisierung (engl. typing) ist stets ein Kompromiss zwischen möglichst strikten statischen Prüfungen und einer die Flexibilität und Ausdrucksstärke der Sprache bewahrenden Sprachform. Es gibt starke Typisierung (strong typing), schwache Typisierung (weak typing), dynamische Typisierung (dynamic typing), statische Typisierung (static typing), explizite Typisierung (explicit typing) und implizite Typisierung (implicit typing). Die Typisierung hängt mit zahlreichen weiteren Aspekte (Polymorphe Werte, Typumwandlung, Russellschen Antinomie, usw.) zusammen.

Bei der Programmierung dient Typisierung dazu, eine formal-korrekte Verwendung von Objekten der Programmiersprachen (wie z. B. Variablen, Funktionen, Objekte) zu erzwingen, um dadurch möglichst viele Laufzeitfehler zu vermeiden.

Eine starke, statische Typisierung (strong typing wie z.B. bei Java) kann zahlreiche Fehler bereits beim Compilieren aufdecken. Dennoch werden Test's gebraucht, um mögliche andere, wichtige Fehler und deren Ursachen zu finden. ( siehe z.B. Typisierung )

Im Gegensatz zur starken, statischen Typisierung verzichtet die dynamische, schwache Typisierung (z.B. JavaScript, Python und Ruby) auf eine explizite Typisierung. Die Zuteilung des Typs einer Variablen erfolgt zur Laufzeit eines Programms. Der Typ einer Variablen ergibt sich aus dem Typ des Wertes, der ihr zugewiesen wird. (siehe z.B. Dynamische Typisierung ).

Zu ECMAScript gehört eine dynamische, schwache Typisierung von Datentypen (weak typinges, es gibt kein Typecasting), die für die Programmentwicklung befreiend wirken kann.

Einfache Datentypen sind Zahlen (Number), Zeichenkette (String), Boolsche Werte (Boolean, true, false), null, undefined. Diese können Methoden besitzen, sind aber unveränderlich.

Objekte sind bei ECMAScript veränderbare, über Schlüssel gebundene Sammlungen ( Array, Function, RegEx, Object ).

Ein Objekt ist ein Container mit Eigenschaften, wobei eine Eigenschaft einen Namen (bel. String, auch leerer String) und einen Wert (bel., außer undefined) besitzt. Objekte sind klassenfrei. Objekte sind nützlich, um Daten zu sammeln, zu strukturieren. Objekte können andere Objekte enhalten.

John Lennon: And you think you're so clever and classless and free
Funktionen (einführend) Was bedeutet "Hochziehen"?

ECMAScript ist eine prototypische Sprache und Objekte können direkt von anderen Objekten erben. Bei ECMAScript sind Funktionen Objekte. Objekte sind Sammlungen von (Namen/Wert)-Paaren. Implizite Deklaration werden oft als Prototyp bezeichnet. Wird z.B. ein ECMAScript Funktionsobjekt angelegt, so wird bei der Funktionserzeugung ein Funktions-Konstruktor-Code, etwa this.prototype = {construktor: this}; ausgeführt. Das prototyp-Objekt ist der Ort, wo ererbte Elemente abzulegen sind.

Bei der Programmentwicklung wird versucht, eine Reihe von Anforderungen an die Applikation auf Methoden (Funktionen) und Datenstrukturen abzubilden. Funktionen fassen eine Reihe von Anweisungen zusammen. Eine Funktion kann wieder verwendet werden. Der Funktionsrumpf bildet den Sichtbarkeitsbereich für die Funktionsparameter und für weitere funktionsinterne Variablen, Objekte, Funktionen.

Die aus Object-Literalen erzeugten Objekte sind mit Object.prototypeverknüpft (prototypische Delegation). Die aus Function-Literalen erzeugten Funktionen sind mit Function.prototype verknüpft (das selbts wieder mit Object.prototype verknüpft ist). Zu jeder Funktion gehört als Wert von Function.prototype eine construktor-Eigenschaft.

Ein vereinfachter Funktion-Ausdrucke sieht etwa so aus: function foo() {}. Um zu verdeutlichen, daß Funktionen Werte sind, ist es günstiger, anstelle eines Funktion-Ausdruckes die folgende Funktion-Anweisung var foo = ...zu verwenden, die der Variablen foo als Wert das "Funktionsliteral" zuordnet.

Funktionen werden in ihrem Geltungsbereich "Hochgezogen", dadurch wird die Vorschrift gelockert, daß Funktionen vor der Verwendung definiert sein müssen.

Code Snippet: Funktion-Anweisung, hidden Variablen
  1. // Funktion-Anweisung
  2. var foo = function foo() {};
  3.  
  4. // Die folgende Funktion kann sich auf die Umgebung auswirken,
  5. // führt aber keinen neuen globalen Variablen ein
  6. (function () {
  7.   var hidden_variable;
  8.   // ...
  9. })();
Funktionen (Merkregeln) Was ist da besonderes?
Ausführungskontexte (execution context) ... Was meint __proto__?

Zu einem ECMAScript-Code gibt es einen Ausführungskontext. Code wird in einem zugehörigen "execution context" ausgewertet. Jeder Funktionsaufruf wird in dem Ausführungskontext der aufgerufenen Funktion ausgeführt. Zu einem Ausführungskontext gehören
Variablenobjekt {Variablen, Funktionsdeklaration, Parameter, ...},
Scope-Chain [Variablenobjekt + alle übergeordneten Scopes],
this-Wert gebunden an das Kontextobjekt, weitere Zustände sind optional.

Globaler Ausführungskontext:

  var x = 10;
 
  function fn() {}    // Funktionsdeklaration (wird gespeichert)
 (function fa() {});  // Funktionsausdruck (wird nicht gespeichert)

 // (this.x == x)      ist  true
 // (window.fn == fn)  ist  true
 // fa  is nicht definiert

Das folgende Beispiel (nach Dmitry A. Soshnikov) soll so nicht verwendet werden und dient lediglich dem Verständnis und der Verdeutlichung des verborgenen __proto__-"Zeigers".

Bei einem Methoden-Aufruf im Objekt wird this an das Objekt {...} gebunden. Dies triff auf die folgende Funktion calculate zu.

 var a = { x: 10,  calculate: function (z) { return this.x + this.y + z } };
 
 var b = { y: 20,  __proto__: a }; // __proto__ verweis auf a

 var c = { y: 30,  __proto__: a }; // __proto__ verweis auf a
 
 // Rufe die vererbte Methode auf

 b.calculate(30); // ergibt 60
 c.calculate(40); // ergibt 80
Funktionen (Argumente, Exceptions) ... flexible Funktionsargumente?

arguments ist (leider nur) ein array-artiges Objekt, das die Parameterwerte enthält. arguments besitzt die length-Eigenschaft, aber alle Array-Methoden fehlen.

Beispiel: Es soll die Summe der Parameterwerte berechnet werden. calc1 nimmt ungeprüft jedes arguments[i]. number und string können addiert werden, für einen Array wird automatisch "toString()" duchgeführt.
calc2 verwendet von den arguments[i] nur die number

Ausnahmen (Exceptions) sind ungewöhnliche, aber nicht völlig unerwartete Pannen, die den normalen Progammfluß stören. Eine solche Panne soll bei calc3 eine Ausnahme auslösen. calc3 zeigt die Exception-Verwendung mit try, throw, catch.

Code Snippet: Berechne die Summe der Argumentwerte
  1. // Bilde die Summe von Parameterwerten
  2. var calc1 = function (){
  3.   var i, sum = 0;
  4.   for(i = 0; i < arguments.length; i += 1) {
  5.     sum += arguments[i];
  6.   } return sum;
  7. };
  8.  
  9. var calc2 = function (){ // addiere number's
  10.   var i, w, sum = 0;
  11.   for(i = 0; i < arguments.length; i += 1) {
  12.     w = arguments[i];
  13.     sum += (typeof w === 'number') ? w : 0;
  14.   } return sum;
  15. };
  16.  
  17. var calc3 = function (){ // teste Parametertypen
  18.   var  i, w, sum = 0;
  19.   for(i = 0; i < arguments.length; i += 1) {
  20.     w = arguments[i];
  21.     if (typeof w !== 'number' && typeof w !== 'string') {
  22.       try {
  23.         throw {
  24.           name: '*** typeError: ' + typeof w,
  25.           message: ' beim Argument: ...' + w,
  26.           func: ' in Funktion calc3 '
  27.         };
  28.       } catch (e) { return e.name + e.func + e.message;
  29.       }   
  30.     } else { sum +=  w;
  31.     }
  32.   } return sum;
  33. };
  34.  
  35. var sum1 = calc1(1, 2, 3, 'a',['b'], {c:9}, 4);
  36. var sum2 = calc2(1, 2, 3, 'a',['b'], {c:9}, 4);
  37. var sum3 = calc3(1, 2, 3, 'a',['b'], {c:9}, 4);
  38.  
  39. var s = sum1+'\n'+sum2+'\n'+sum3;
  40.  
  41. document.getElementById("sum_args").innerHTML = s;

Liefert die Ausgabe:









Eingebaute Funktionen für Array's Prototype für "del" ...

Für Array's gibt es bereits zahlreiche,eingebaute Methoden, wie .concat(), .join(), .pop(), .push(), .reverse(), .shift(), .slice(), .sort(), .splice(), .unshift().

Aktuell stehen bei den 'gängigen' Browsern ( Quelle: http://kangax.github.com/es5-compat-table/ ) auch .every, .some, .forEach, .map, .filter, .reduce, .reduceRight, .indexOf, .lastIndex 'nativ' zur Verfügung, die (falls erforderlich für IE < 9) auch per Array.prototype.every, Array.prototype.some, Array.prototype.forEach, Array.prototype.map, Array.prototype.filter, Array.prototype.reduce, Array.prototype.reduceRight, Array.prototype.indexOf, Array.prototype.lastIndex vom Entwickler definiert werden können.

Die vorhandene Methode .slice(), greift einen Bereich von Elementen heraus. Es gibt aber keine eingebaute Methode .del, die einen Bereich von Elementen löscht. Möglich ist die folgende Funktionsdefinition für del, bei der die ("umständliche") prototype-Schreibweise sichtbar ist.

Array.prototype.del = function(start, end) {
  var r = this.slice(0, start);
  if(!isNaN(end)) { r = r.concat(this.slice(end+1)); }
  return r;
};

Günstiger ist die Nutzung der User-Function method, die dann für zahlreiche weitere Fälle zur Prototype-Funktionsdefinition verwendet werden kann.

Code Snippet: definiere arr.del() als Gegenstück zu arr.slice()
  1. Function.prototype.method = function(name, func) {
  2.   if (!this.prototype[name]) {
  3.        this.prototype[name] = func;
  4.   }
  5. };
  6.  
  7. Array.method('del', function(start, end) {
  8.   var r = this.slice(0, start);
  9.   if(!isNaN(end)) { r = r.concat(this.slice(end+1)); }
  10.   return r;
  11. });
  12.  
  13. var arr1 = ['s0', 's1'].concat(["s2", 3, 4]);
  14. var arr2 = arr1.del(1,3);  //['s0', 4]
  15.  
  16. var s = "Gegebener Array ist     arr1 = ["+ arr1.join(', ') + "]" +
  17.         "\nnach Elementlöschung    arr1.del(1,3) = [" + arr2.join(', ') + "]";
  18. document.getElementById("del").innerHTML = s;

Liefert die Ausgabe:







Objektliterale (einführend) Was ist ein ECMAScript-Objekt?

ECMAScript verfügt über ausdrucksstarke literale Objektnotation (Schlüssel/Werte-Paare)

Ein literales Objekt ist durch umschließende {...} gekennzeichnet. Ein literales Objekt kann überall dort stehen, wo ein Ausdruck steht. Objekte werden stets per Referenz übergeben. Es findet keine inhaltliches kopieren statt.

Code Snippet: Objektliterale
  1. var empty = { };
  2.  
  3. var obj   = {"first-name": "Manfred", geboren: 1942};
  4.  
  5. var erde  = { r:6373,
  6.     umfang:function(r){return 2*r*Math.PI.toFixed(1);}
  7. };
  8.  
  9. var flug = { number: 4711,
  10.     airline: "Lufthansa",
  11.     abflug:  { city: "Köln",time: "2010-04-12 14:55" },
  12.     ankunft: { city: "Ffm", time: "2010-04-12 16.00" }
  13. };
  14. var s = "Der Erdumfang [km] ist " + erde.umfang(erde.r);
  15.    s += "\n" + obj["first-name"] +
  16.         " fliegt mit der " + flug.airline +
  17.         " nach " + flug.ankunft.city;
  18.    s += "\nKein Type-Error:" + (flug.x && flug.x.y);
  19.         
  20. document.getElementById("obj_flug").innerHTML = s;

Liefert die Ausgabe:





Reflexion (Abfrage von Datentypen, Objektzugehörigkeit) Was ist Reflexion?

In der Programmierung bedeutet Reflexion (engl. reflection) bzw. Introspektion, dass ein Programm seine eigene Struktur kennt und diese, wenn nötig, modifizieren kann. So kann die CPU den Opcode als Daten betrachten, eine virtuellen Maschine kann zur Laufzeit ergänzende Informationen abzufragen. Reflexion ermöglichen eine größere Laufzeitflexibilität, weil ergänzende Informationen dynamisch aufgerufen und ggf. Typen und Objekte sogar dynamisch strukturiert werden können. (siehe z.B. Reflexion ).

ECMAScript hat zur Laufzeit die .hasOwnProperty(), .instanceof, .propertyIsEnumerable() Abfragemöglichkeit. Die Typabfrage mit typeof kann zuverlässiger gemacht werden:

function type_of(v) {
  if (v === null) { return 'null'; }
  if(typeof v === 'undefined'){ return 'undefined'; }
  return Object.prototype.toString.call(v).slice(8,-1).toLowerCase();
}
Delegation (hier Prototypkette bei Array, Object) Was bedeutet Delegation?

Die Prototyp-Beziehung ist eine dynamische Beziehung. Wenn aus einem Objekt ein Eigenschaftswert abgerufen wird und dieser Eigenschaft nicht im Objekt existiert, so wird versucht, den Eigenschaftswert im Prototyp-Objekt ("Vorgänger-Objekt") abzurufen. Die ("zurückgehende") Prototyp-Verkettung endet bei Object.prototype. Ist die angeforderte Eigenschaft innerhalb der Prototyp-Kette nicht zu finden, so ist der Rückgabewert undefined.

Array und Object sind Objekte. Es gibt Ähnlichkeiten und Unterschiede. Array's werden verwendet, wenn die Eigenschaftsnamen sequenzielle Integerwerte sind (z.B. 0, 1, 2, ...) andernfalls Objektliterale. Array erbt die verfügbaren Methoden von Array.prototype. Für Array's gibt es bereits zahlreiche,eingebaute Methoden, wie .concat(), .join(), .pop(), .push(), .reverse(), .shift(), .slice(), .sort(), .splice(), .unshift().

Array hat die Eigenschaft .length, das dem (max. Index + 1) entspricht. arr.length kann (max. 4294967295) gesetzt werden, bei Vergrößerung wird nicht mehr Platz allokiert, bei Verkleinerung werden "überschüssige" Elemente gelöscht.

Object erbt die verfügbaren Methoden von Object.prototype.

Code Snippet: Vergleiche Array und Object
  1. var arr = [       'zero',       ,       'two' ];
  2. var obj = { '0' : 'zero', '1': 1, '2' : 'two' };
  3.  
  4. arr[arr.length] = 3; arr.push('4',5); delete arr[4];
  5. obj.three = 3;       obj['4'] = 4;
  6.  
  7. var s = "arr.toString() = " + arr.toString() +
  8.         "\narr.length = " + arr.length +
  9.         "\nobj = " + obj +
  10.         "\nobj.length = " + obj.length;
  11.         
  12. document.getElementById("arr_obj").innerHTML = s;

Liefert die Ausgabe:








Prototype-basierte Programmierung (allgemein) Was bedeutet das?

Prototyp-basierte Programmierung (klassenlose Objektorientierung) verzichtet auf das Sprachelement der Klasse (siehe z.B. Prototyp-basierte Programmierung Prototyp Entwurfsmuster ).

Für Vererbungsverfahren und das Hinterlegen von gemeinsame Eigenschaften verwendet ECMAScript eine endliche Prototyp-Kette (prototype chain). Nach dem ECMAScript-Standard ist jedes Objekt ist mit einem Prototyp-Objekt verknüpft, von dem es Eigenschaften erben kann. Alle mit Objektliteralen erzeugten Objekte sind mit Object.prototype verknüpft. Wiederverwendbarer Code wird über die Prototyp-Kette erreicht (Delegation basierte Vererbung, Prototyp-basierte Vererbung).

Eine prototyp-basierte Programmierung kommt ohne (Erzeuger-) Klassenhierarchie aus und erlaubt ein (schnelles) erzeugen komplexer Objekte, die Erzeugung und Einbindung von Unterklassen zur Laufzeit.

Objekte werden durch das Clonen bereits existierender Objekte erzeugt. Alle bestehenden Objekte können Prototypen neuer Objekte sein. Beim Clonen werden alle Eigenschaften (Attribute und Methoden) vom Prototyp-Objekt geerbt und durch eigene Eigenschaften erweitert

__prototype__ entspricht bei ECMAScript einem verborgenen Zeiger zum existierenden Objekt. Dadurch kann der Umfang von eingebauten, verfügbaren Methoden nachträglich erweitert werden.

ECMAScript erlaubt die Type-Erweiterung (prototype). Auch die grundlegenden Typen ( Function, Array, String, Number, Boolean, RegEx ) können mit Hilfe von prototype User-Methoden hinzu gefügt werden

Prototype-basierte Programmierung (umständlich) zu Child.prototype ...

ECMAScript kann zwar new Boolean(false), new Number, new String verwenden, was nicht empfehlenswert ist.

Der new-Operator erzeugt ein neues Objekt, das vom prototyp-Member des Operanden erbt und dann den Operanden aufruft, um das neue Objekt an this zu binden. Das gibt dem Operanden (der besser eine Konstruktorfunktion wäre) die Gelegenheit, das neue Objekt anzupassen, bevor es zurück gegeben wird. Wird aber der new-Operator vergessen, so entsteht ein einfacher Funktionsaufruf und this wird ohne Warnhinweis an das globale Objekt gebunden und nicht an das neue Objekt.

Douglas Crockford: "Vermeiden sie new Object und new Array. Verwenden sie statdessen { } und [ ]"

Code Snippet: Child.prototype = obj1; obj2 = new Child();
  1. var s, obj2, obj1 = { a : 11, 'b' : 12, c: 13 };
  2.  
  3. function Child() {
  4.     this.c = 23;
  5.     this.d = 24;
  6. }
  7. Child.prototype = obj1;
  8. obj2 = new Child();
  9. delete obj1.a;  // löscht       obj1.a
  10. delete obj2.b;  // löscht nicht obj1.b
  11. obj2.a = '21';  // neu          obj2.a
  12.  
  13. s = "obj1.a = " + obj1.a + "\nobj1.b = " + obj1.b +
  14.      "   obj1.hasOwnProperty('b') = " + obj1.hasOwnProperty('b') +
  15.      "\nobj1.c = " + obj1.c + "\nobj1.d = " + obj1.d + "\n" +
  16.      "\nobj2.a = " + obj2.a + "\nobj2.b = " + obj2.b +
  17.      "   obj2.hasOwnProperty('b') = " + obj2.hasOwnProperty('b') +
  18.      "\nobj2.c = " + obj2.c + "\nobj2.d = " + obj2.d;
  19. document.getElementById("new_Child").innerHTML = s;

Liefert die Ausgabe:







Prototyp-basierte Programmierung (elegant) Object.create, differenzielle Vererbung

Für Programmieranfänger von ECMAScript kann "new" seine Tücken haben. Objekte werden stets per Referenz übergeben. Es findet keine inhaltliches kopieren statt. Anstelle eines unübersichtlichen prototype ist es günstiger, eine "Factory-Methode" Object.create() zu verwenden. Dadurch wird das prototype und new in Object.create() versteckt. Dadurch vereinfachen sich Datenkapselung (Encapsulation, information hiding), Vererbung (Inheritance), object composition ("has a" relationship), Association ("sending a message", "invoking a method", "calling a member function"), Aggregation (object contains another object), Polymorphism (dynamische Types).

Code Snippet: Object.create(o)
  1. if (typeof Object.create !== 'function') {
  2.   Object.create = function( o ) {
  3.     function F(){}
  4.     F.prototype = o;
  5.     return new F();
  6.   };
  7. }
  8.  
  9. var obj1 = { a : 11, 'b' : 12, c: 13 };
  10. var obj2 = Object.create(obj1);
  11.   
  12. obj2.c = 23;  
  13. obj2.d = 24;
  14. delete obj1.a;  // löscht       obj1.a
  15. delete obj2.b;  // löscht nicht obj1.b
  16. obj2.a = '21';  // neu          obj2.a
  17.   
  18. var s = "obj1.a = " + obj1.a + "\nobj1.b = " + obj1.b +
  19.   "   obj1.hasOwnProperty('b') = " + obj1.hasOwnProperty('b') +
  20.   "\nobj1.c = " + obj1.c + "\nobj1.d = " + obj1.d + "\n" +     
  21.   "\n\nobj2.a = " + obj2.a + "\nobj2.b = " + obj2.b +
  22.   "   obj2.hasOwnProperty('b') = " + obj2.hasOwnProperty('b') +
  23.   "\nobj2.c = " + obj2.c + "\nobj2.d = " + obj2.d;
  24.   document.getElementById("mit_clone").innerHTML = s;

Liefert die Ausgabe:








Beispiele zu den obigen Punkten

Dynamische Datentypen Wie ist der Typ zur Laufzeit?

Damit die Function isArray(x) auch für Array's funktioniert, die in einem anderen Fenster oder Frame definiert wurden, sind zahlreiche Abfragen erforderlich.

Code Snippet: dynamische Typisierung
  1. function isArray(x) {
  2.   return x && typeof x === 'object' &&
  3.   typeof x.length == 'number' &&
  4.   typeof x.splice == 'function' &&
  5.   !(x.propertyIsEnumerable('length'));
  6. }
  7. var x,     s = "<pre>Dynamische Typisierung:\n";
  8.            s += "\nvar x;          // typeof x = "+ typeof x;
  9. x = 2;     s += "\nx = 2;          // typeof x = "+ typeof x;
  10. x += 3.2;  s += "\nx += 3.2;       // typeof x = "+ typeof x;
  11. x = '17.3' + x; // oder x = "17.3" + x;
  12.            s += "\nx = '17.3' + x; // typeof x = "+ typeof x;
  13. x = [0, x];s += "\n\nx = [0, x];     // typeof x    = "+ typeof x;
  14.            s += "\n                // typeof x[1] = "+ typeof x[1];
  15. x = [0, x];s += "\n                // isArray(x)  = "+ isArray(x);
  16. x = {'a':parseInt(x[1],16), 'x':x, f:function(a,b){return a*b;} };
  17.            s += "\n\nx = {'a':parseInt(x[1],16), 'x':x, f:function(a,b){return a*b;} };";
  18.            s += "\n                // typeof x   = "+ typeof x;
  19.            s += "\n                // typeof x.a = "+ typeof x.a;
  20.            s += "\n                // typeof x.x = "+ typeof x.x;
  21.            s += "\n                // typeof x.f = "+ typeof x.f;
  22. s += "</pre>";
  23. document.getElementById("dynamischeTypisierung").innerHTML=s;

Liefert die Ausgabe:

Objektliterale (Beispiel) "Tiefenkopie" mit Object.create?
Code Snippet: Object.create(o)
  1. if (typeof Object.create !== 'function') {
  2.   Object.create = function( o ) {
  3.     function F(){}
  4.     F.prototype = o;
  5.     return new F();
  6.   };
  7. }
  8.  
  9. var obj1 = { a : 11, 'b' : 12, c: 13 };
  10. var obj2 = Object.create(obj1);
  11.   
  12. obj2.c = 23;  
  13. obj2.d = 24;
  14. delete obj1.a;  // löscht       obj1.a
  15. delete obj2.b;  // löscht nicht obj1.b
  16. obj2.a = '21';  // neu          obj2.a
  17.   
  18. var s = "obj1.a = " + obj1.a + "\nobj1.b = " + obj1.b +
  19.   "   obj1.hasOwnProperty('b') = " + obj1.hasOwnProperty('b') +
  20.   "\nobj1.c = " + obj1.c + "\nobj1.d = " + obj1.d + "\n" +     
  21.   "\n\nobj2.a = " + obj2.a + "\nobj2.b = " + obj2.b +
  22.   "   obj2.hasOwnProperty('b') = " + obj2.hasOwnProperty('b') +
  23.   "\nobj2.c = " + obj2.c + "\nobj2.d = " + obj2.d;
  24.   document.getElementById("mit_clone").innerHTML = s;

Liefert die Ausgabe:



Sichtbarkeit, Closure Was bedeutet Closure?

Der funktion-interne Geltungsbereich von Variablen bewirkt, daß innere Funktionen Zugriff auf die Werte von Variablen in der umschließenden, äußere Funktion (außer this, arguments) haben.

Tennet Correspondence Principle (test of lambda abstraction)
Blocks (aber nicht für return, break, continue, this, arguments, var, function): {…} ≡ (function(){…})();
Expressions (aber nicht für this, arguments: (…) ≡ (function(){return …;})()

Hier ein erstes Beispiel, das die Sichtbarkeit der Variablen x zeigt.

Code Snippet: Sichtbarkeit der Variablen x, s
  1. var teste = function(s) {
  2.   var x = 4;
  3.   function test1() {
  4.    s += "<br />test1: glob. x=" + x;
  5.   }
  6.   function test2() {
  7.    s += "<br />test2: glob. x=" + x;
  8.    var x = 1;
  9.    s += " und nach var x = 1 ist x=" + x;
  10.   }
  11.   function test3() {
  12.    s += "<br />test3: glob. x=" + x;
  13.    var x = x;
  14.    s += " und nach var x = x ist x=" + x;
  15.   }
  16.   test1(); test2(); test3(); return s;
  17. }
  18. document.getElementById("sichtbarkeit").innerHTML = teste('');

Liefert die Ausgabe:

Entwicklungsmuster (Private Variablen) Dies wird empfohlen ...

Anstatt obj mit einem Objektliteral zu initialisieren, kann eine namenlose Funktion aufgerufen werden, die ein Objektliteral zurück gibt (Funktions-Ausführung infolge von () ). Die Variable priv ist für die public-Funktionen verfügbar, aber der umschließende Funktions-Geltungsbereich sorgt dafür, daß priv vor dem Rest des Programmes verborgen bleibt.

Code Snippet (Einfaches Entwicklungsmuster)
  1. var obj = (function() {
  2.   var priv = ""; // Private Members
  3.   //function show_priv() { alert(priv); }
  4.   return { // Public members
  5.     pub: "public-String",
  6.     get:  function() {           return priv; },
  7.     set:  function(v){ priv = v; return this; },
  8.     add:  function(s){ priv += s; return this; },
  9.     del:  function(start,end){
  10.           priv = priv.substr(0,start) + priv.substr(end);
  11.           return this;
  12.     },
  13.     trim: function() { priv = priv.replace(/^\s+|\s+$/g,"");
  14.           return this;
  15.    }
  16.   };
  17. }());
  18.  
  19. var s1 = 'hallo', s2 = 'welt', len1 = s1.length,
  20.   n1 = obj.set(' '+s1).add(s2+' ').trim().get().length,
  21.   n2 = obj.set(s1).add(obj.pub).add(s2)
  22.           .del(len1,len1+obj.pub.length).get().length,
  23.   txt = "n1 = " + n1 + "<br />n2 = " + n2;
  24. document.getElementById("entwicklungsmuster").innerHTML = txt;
// Beispiel:
var s1 = 'hallo', s2 = 'welt', len1 = s1.length,
    n1 = obj.set(' '+s1).add(s2+' ').trim().get().length,
    n2 = obj.set(s1).add(obj.pub).add(s2)
            .del(len1,len1+obj.pub.length).get().length,
    txt = "n1 = " + n1 + "<br />n2 = " + n2;
    document.getElementById("entwicklungsmuster").innerHTML = txt; 

// Liefert die Ausgabe:












Vectoren und Matrizen Wie geht es mit Matrizen?

Code Snippet: Vectoren und Matrizen
  1. Array.vector = function(n, val) {
  2.   var i, vec = [];
  3.   for ( i=0; i < n; i += 1) { vec[i] = val;
  4.    } return vec;
  5. };
  6. Array.matrix = function(m, n, val) {
  7.   var a, i, j, mat = [];
  8.   for ( i=0; i < m; i += 1) { a = [];
  9.     for ( j=0; j < n; j += 1) {
  10.       a[j] = val;
  11.     } mat[i] = a;
  12.   } return mat;
  13. };
  14. Array.identity = function(n) {
  15.   var a,i,j, m = n, mat = [];
  16.   for ( i=0; i < m; i += 1) { a = [];
  17.     for ( j=0; j < n; j += 1) {
  18.       a[j] = (i===j) ? 1 : 0;
  19.     }  mat[i] = a;
  20.   } return mat;
  21. };
  22. Array.prototype.to_string = function(a,b,c) {
  23.   var i, r = [], m = this;
  24.   a = a || '[[';
  25.   b = b || '],[';
  26.   c = c || ']];';
  27.   for ( i=0; i < m.length; i += 1) {
  28.     if(!m[i].length) { r[i] = m[i];
  29.     } else { r[i] = m[i].join(',');
  30.     }
  31.   } return a + r.join(b) + c;
  32. };
  33.  
  34. var br = '<br />';
  35. var vec = [1,2,3];
  36. var arr2 = [[1,2,3],[4,5,6,7],[8,9]];
  37.  
  38. var s =  vec.to_string('var vec  = [', ',', ']') + '<br />' +
  39.         arr2.to_string('var arr2 = [[')+ '<br />' +
  40. Array.identity(3).to_string(
  41.   'Array.identity(3) = [<br />  [',
  42.   '],<br />  [',
  43.   ']<br />];'
  44. );
  45. document.getElementById('array_test').innerHTML = s;

Liefert die Ausgabe: