[social buttons]

JavaScript

Die mittlerweile populärste Programmiersprache der Welt

JavaScript für Hartz IV Empfänger [die ersten Zeilen]

MDN logo.js

Die Beispiele sind (fast) alle mit Copy + Paste auf der Browserkonsole ausführbar.

(von 2012)

Ich wünsche gute Unterhaltung.


Vorwort?

C Programmierung mit der JSAPI

Wer wollte schon nicht immer schon mal mit der Mozilla Firefox Engine programmieren? Sie ist übrigens auch die aktuelle Version der ersten JavaScript Engine, die je geschrieben wurde. Aktuell fliessen ihre Funktionen noch nicht in die Dokumentation ein. Aber sobald ihr JS_Funktionen lesen könnt, habe ich bereits welche eingetragen, um das Verständnis für JavaScript noch weiter zu schärfen.

		#include <jsapi.h>
		JSClass global_flags = {
		    "global",	// "[object global]";
		    JSCLASS_GLOBAL_CLASS,
		    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, // immer 3x PropertyStub, 1x StrictPropertyStub
		    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
		};
		#define MEMORY 8*1024*1024;
		JSRuntime *rt = JS_NewRuntime(MEMORY); // achtung: heap
		JSContext *cx = JS_NewContext(rt, MEMORY);  // 
		JS_SetVersion(JSVERSION_LATEST);
		JS_SetOptions(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT);
		JSObject  *global = JS_NewGlobalObjectAndCompartment(cx, &global_flags, NULL);
		const char *script = "'use strict'";
		jsval rval;
		JS_EvaluateScript(cx, global, script, strlen(script), &rval);
		fprintf(stdout, "%s\n", JS_EncodeString(cx, JS_ValueToString(rval)));
	    

Wenn ihr solchen C Code lest, dann handelt es sich um SpiderMonkey. Wenn ihr solchen Code lest, dann um V8.

		Handle<Value> NativeFunktion (const Arguments &args) {
		    HandleScope scope;
		    Handle<Object> obj = Object::New();
		    scope.Close(obj);
		}
	    

Bislang handelte dieses Dokument von reinem JavaScript. Ich denke mir aber, JavaScript wird verstaendlicher, wenn ich euch das ebenfalls aufschreibe. Darum werde ich jetzt von Zeit zu Zeit damit forfahren. Ausserdem habe ich hier viel Text aufzuräumen.

SpiderMonkey

JavaScript Versionen und Geschichte

History of JavaScript: Ich lerne sie immernoch (es sind halt ein paar Jahre). Ich schreib das erst auf, wenn ich es richtig auswendig wiedergeben kann. (Früher war das Webtime/Time to Market, es ging schnell mit dem Release, dann kam Microsoft naechstes Jahr mit JScript, dann musste ein Standard her, um nicht gegen MS zu verlieren, und weil das W3C Netscape zur Hoelle schicken wollte, kriegte man erst bei der ECMA einen Standard, aber das ist ja nicht alles).

Zum Namen JavaScript. JavaScript ist? Eine Implementation (jsapi, jscript, etc.) ECMAScript ist? Name des Standards selbst.

ES6 auch != Harmony != ES7

Moment, ich bin dran. Die Zukunft JavaScripts besteht aus mehreren Teilen. Brendan sagte bei BrazilJS, dass Object.observe es nicht mehr in ES6 geschafft hat, aber nach Harmony, was auf ES7 schliessen laesst...Ich bleibe dran und finde raus, was wann kommt.

(Wenn ich zur Zeit mehr Zeit haette, haette ich das laengst erforscht, aber die Zeit kommt...)

ECMAScript next (ES6)

Better Code Generation and Application Language.

Soll 2013 finalisiert werden. Ich denke mal, es ist richtig, sich jetzt schon einzuarbeiten, um das mitverfolgen zu können...Es ist noch nicht Thema dieses Dokuments, wird es aber nach und nach werden.

Inzwischen: Andere haben Module-Syntax Support, Class Support, ES6-Shim und das bereits hinter sich und sich eingearbeitet.

Von Brendan Eichs Talk bei BrazilJS abgeschrieben: (*)

(*) Es gibt noch einen zweiten Teil des Videos, es brach gerade ab, wo er bei JVM/Bytecode und JS/JIT war, es um TypedArrays fuer einen Binaeren Speicher geht, genau das, was hier noch nicht drankommt. Wenn ihr nach dem BrazilJS Talk guckt, beachtet, dass es einen zweiten Teil gibt. Der fiel mir erst gestern (21. Nov.) auf. Da gibt es einen Bezug zu EMSCRIPTEN, was ich mir jetzt auch mal endlich angucken MUSS. (Resultat: emscripten ist _noch_ nichts fuer mich.)

	Official Goals: a better language for
	
  • applications
  • libraries
  • code generators

Diese Ziele verfolgt das Kommitee schon einige Jahre. Bei ES4 stand das auch auf der Tafel, hehe.

Leider kann man das noch nicht ausprobieren, jedenfalls nirgendwo richtig, am Compat Table kann man sehen, dass einige wie yield z.B. in C++ implementiert werden muessen, ein paar andere kann man aber in ES5 schreiben. Vielleicht werde ich das auch noch hinkriegen.

ES6 compat-table von kangax.github.com/es5-compat-table/es6/

wiki.ecmascript.org - hier gibt es die Papiere fuer alle Funktionen.

ES4 (Harmony)

"Aufgeschoben ist nicht aufgehoben"

Warum man doch nicht übereinkam, und ES4 wieder gecanceled wurde, und warum ES5 (ES3.1 ist Crocks Proposal, sowie Allens Meta Object API) erstmal gemacht wurde.

Was Crockford wirklich gemacht hat, ob er Microsoft ueberzeugt hat, gegen Harmony zu stimmen, weil Microsoft ebenfalls nicht ueberzeugt klang, aber erst dafuer stimmen wollte? Ja. In "The State and Future of JavaScript" bei der YUIConf 2009 gibt es die ganze Geschichte. Er gibt da ausserdem eine wunderbare Auskunft ueber den IEEE754 und IBMs damaliges IEEE754r Proposal. Er beginnt damit, wie TC-39 damals aus vier Leuten bestand und es dann mehr wurden, und wie man halt nicht uebereinkam. Sollte man sich angucken, wenn man was ueber die Zusammenhaenge lernen will.

Bei Brendan Eich im anderen Room am anderen Tag:

Ein anderer Teil der Geschichte und implementationsspezifische Sachen zu Macromedia Actionscript, Namespaces, ueber die Harmony Sprache, die heute immernoch in etwa das gleiche ist.

Worum es genau ging, demnächst an dieser Stelle.

ES3

ES5 Standards Mode ist das neue ES3

ES3 selbst ist nur noch fuer aeltere Generationen interessant und da gibt es ES5-polyfills, ES5-shims, exCanvas, html5-shims, es6-shims und so weiter.

Das Gebiet ES3 duerfte voellig er- und geschlossen sein.

ECMAScript 5.1

5.1 Edition / June 2011

Brendan Eich hat den bärtigen Magier auf der Brücke, die er passieren wollte, gesehen, er hat die Warnungen vor den Trollen, die der Magier aussprach, verstanden. Crockford stand auf der Brücke und warnte. Und wenn ich das so mitverfolge, war es richtig. Anstatt Harmony wurde EcmaScript 3.1 zusammen mit der Meta-Object API und ein paar anderen Funktionen, nach einer Konferenz im Jahr 2008er, als EcmaScript 5 veröffentlicht.

Aktuelle Fassung online: http://ecma-international.org/ecma-262/5.1/

ECMAScript 5 standards mode

Ist eigentlich == ES3, nicht === ES3, beinhaltet Bugfixes und neue Methoden. Ist so rueckwaertskompatibel wie nur moeglich.

Sinvoll oder nicht sinnvoll? Sinnvoll. Hat genau ausgereicht, um die Sprache richtig gewaltig nach vorne zu bringen. Jetzt haben sie alle Jahre die gleiche gelernt.

next

Grins. Wo kriegt man das? Ich will die Beispiele abspielen. Das ist übrigens wieder dieses Harmony. 7 oder 8 oder was Harmony macht. 10.

ECMAScript 5 strict mode

Auf den Strict Mode haben viele JavaScript Programmierer gewartet, kein this mehr, was auf window zeigt, sondern this was auf undefined. Und eine Menge Bugfixes. Sowie Exceptions statt Silent Errors

Da, wie oben zu lesen, das Kommitte vor Crockford und So aus einer handvoll Leute bestand, sollte man nicht erwarten, dass es dermassen viel zu besprechen gegeben hatte, dass einem was besseres eingefallen waere.

Lexikalischer Scope. Mit dem und der Hilfe von Object.freeze kann man selbstverteidigende Objekte schreiben. Darauf gehe ich noch ein, zur Zeit lerne ich das hinzu, und bald noch viel genauer.

	
	    "use strict"; // File Form
	    function Constructor() {
		this.name = "value";
	    }
	    var o1 = new Constructor();
	    var o2 = Constructor(); // throwt einen TypeError: Can not set property ~name~ of undefined
    	

Dynamischer Scope: Wird nicht zur kompilierzeit, sondern zur Laufzeit hergestellt. (Jetzt liegt es mir auf der Zunge und ich kommt nicht drauf. Dafür verlinke ich das, was es mir das letzte mal hervorbrachte. Probleme? Wikipedia weiß es sofort!

Dynamisches this: In JavaScript gibt es Methoden, die mit .apply, oder .call oder .bind gerufen werden, die this dynamisch zur Laufzeit binden. Es gibt ein paar alte with Statements, die dynamisch da was erstellen.

Die neue ES5 strict mode Version erlaubt es, manipulationssichere Objekte zu erzeugen, die nicht einfach geändert werden koennen und in einem sicheren, nicht globalen Scope laufen.

ES3+ES5 Standards Mode (manipulierbar) ES5 strict mode (manipulationssicher)
    function makeCounter () {
	var counter = 0;
	return {
	    incr: function () { return ++counter; },
	    decr: function () { return --counter; }
	};
    }
    // makeCounter: Mark S. Miller, Google
    var counter = makeCounter();
    counter.incr = function () {
	return "tilt";
    };
    counter.incr();
    // tilt
    function makeCounter (counter) {
	"use strict";
	counter = counter || 0; // waiting for default parameters in es6 #makeCounter(counter=0)
	return Object.freeze({
	    incr: function () { return ++counter; },
	    decr: function () { return --counter; }
	});
    }
    // makeCounter: Mark S. Miller, Google.
    var counter = makeCounter();
    counter.incr = function () { 
	return "tilt"; 
    }; 
    // Die neue Zuweisung wird nicht funktionieren, 
    // da Object.freeze() auf dem Counter Objekt gerufen wurde
    // Die Antwort ist nicht "tilt" wie in ES3 oder ES5 Standards/Strict OHNE Object.freeze
    counter.incr();
    // 1
    // (dank Object.freeze)

(Man kann auch im Standards Mode freezen, nicht mißverstehen)

Mehr über ES5 Security demnächst. -- Daran zu erkennen, dass ich diese Aussage aufräume. Ansonsten gibt es bislang erste Ergebnisse im Kapitel Sicherheit kurz vorm Ende. Was ich, da es Sinn und Zweck von ES5 ist, JavaScript mehr und mehr in eine Ocap Language zu transformen, dann bald nach oben hebe und in allen Kapiteln verbauen werde.

(Ihr muesstet mal Millers ES6 Security sehen, ich laechele, lese ich mich selbst.)

Oder

	    (function () {
		"use strict"; // Function Form 
		if (function () { return this; }()) {
		    alert("Please Upgrade your Internet Explorer");
		} else {
		    // strict mode
		}
	    }());
	

"use strict"; lautet das Pragma, was in File oder Funktion stehen kann. Empfohlen wird die Funktionsform, weil in der "Fileform" alle Skripte in einer Datei ab dem Pragma dann strict interpretiert werden würden, was bei Mashups mal zu dem einen oder anderen Error führen könnte.

	    function Strict () {
		"use strict"; // Function Form (d.h. nur Strict () ist strikt)
		    this.name = "Edward";	
	    }
	
	    var a = new Strict(); // Geht natuerlich
	    console.log(a.name);
	    try {
	        var b = Strict(); // hier ist this undefined. Beachtet den Error.
		    console.log(b.name);
	    } catch(e) {
		    console.log(e.name+": "+e.message);
	    }
	
	    // Edward
	    // TypeError: Cannot read Property 'name' of undefined
	

new_constructor

Gibt es in "Act III: Function the Ultimate" (Links am Schluss)

	    function needs_no_new () {
		"use strict";
		return {
		    __proto__: needs_no_new.prototype,
		    name: "Edward"
		};
	    }
	

(Habe hier was geloescht. Hier geht es weiter mit strict mode)

Bedeutet, alle Funktionen, die man so aufruft haben kein this mehr ans global Objekt gebunden. In so einem gebundenen Fall sind die vars immer lokal geblieben, aber alles, was mit this zu tun hatte, ging ans globale Object.

	
	    var demonstrate1 = function () {
		console.dir(this);
	    };
	    demonstrate1();
	    // window
		    
	    var demonstrate2 = function () {
		"use strict";
		console.dir(this);
	    };
	    demonstrate2();
	    // undefined
	    
	    // Natuerlich passiert bei richtiger Verwendung von new nie was 
	    var Demonstrate1b = function () {
		console.dir(this);
	    };
	    new Demonstrate1b();
	    // Object
	    

	    var Demonstrate2b = function () {
		"use strict";
		console.dir(this);
	    };
	    new Demonstrate2b();
	    // Object
	
	

Da muss man sich keine Sorgen mehr um den Scope des Callbacks machen, solage die Funktion, in der er gerufen wird "use strict" ist.

	    // Im strict mode gibt es lieber laute Failures als silent Errors
	    
	    throw new TypeError("Ich hatte was anderes erwartet!");
	    
	    // TypeError: Ich hatte was anderes erwartet!
	

SyntaxError, TypeError, RangeError, ReferenceError, Error. In ES5 gibt es vernünftige Fehlerfunktionen.

ECMAScript 5 hat aber noch viel mehr zu bieten.

Eine andere Quelle für EcmaScript 5 gibt es hier: Crockford on JavaScript - Level 7: ECMAScript 5: The New Parts. Am Ende dieses Files gibt es ein Kapitel mit allen Links unter anderem zu diesem Video und den anderen.

	    var isStrict = function () {
		return !this;
	    };
	    var ists = isStrict();	// sind wird im strictmode ist !this true geworden
	
	    var hasStrict = function() {
		"use strict";	// einschalten
		return !this; // !window wird false !undefined wird true
	    };

	    if (!hasStrict()) {
		// Arm
		console.log("this browser supports only standards mode");
	    } else {
		// Macht
		console.log("this browser supports strict mode");
	    }
	
	    // Herausgefunden habe ich das bei Crockford, der hat auch
	    // so Funktionen, und sagt, dass im strict mode ja undefined
	    // das this ist und !this dann true ist, oder !window halt false.
	    // Als ich das selbst probiert habe, habe ich das auch begriffen.
	    // Also probiert es aus.
	

Der ECMAScript 5 Standard ist für viele (wie mich) immernoch neu. Das JavaScript, was man zehn Jahre kennenlernen musste, hat es auch solange gegeben. ECMAScript 3 wurde 1999 standardisiert. ECMAScript 5 genau zehn Jahre später im Jahre 2009. Mittlerweile in allen aktuellen Browsern vertreten, gibt es den ECMAScript 5 "strict mode" erst ab dem Internet Explorer 10.

	if (function () {"use strict";return this;}()) {
	    // nicht im strict mode
	    alert("Please Upgrade your Internet Explorer");
	}
	

Programme, die im ECMAScript 5 strict mode geschrieben sind, sind sogar zum Standards Mode kompatibel. Man programmiert einfach ohne ein paar bad parts. Solange man sich als Programmierer nicht auf das Pragma verlaesst, sondern aktiv so, wie der strict mode läuft, programmiert, dass der scope auch ohne Pragma gleich ist, kann da nichts passieren. Der lexikalische Scope wird im strict mode halt geaendert, dass normale function calls nicht mit window laufen, das kann man aber auch aktiv programmierend einfach mit call, apply und bind regeln.

Für alte Browser - was ist das ? - kann man schims und polyfills schreiben. Douglas Crockford gibt in seinem 2010 Talk Level 7: The New Parts übrigens einen Haufen Demos auf den Slides. Ich werde die demnächst auch mal endlich dann doch noch übernehmen. (Alles uebrige ist hier von mir. Crock hat mich inspiriert selbst was zu schreiben.)

	// So in etwa kann man fehlende Methoden selbst definieren
	
	// Ich (andeutend)
	if (typeof Array.prototype.forEach !== "function") {
	    Array.prototype.forEach = function (func) {
		for (var i = 0, j = this.length; i < j; i++) {
		    func.call(this, this[i], i);
		}
	    };
	}
	
	// Crock 
	if (typeof Array.prototype.forEach === "undefined") {
	    Array.prototype.forEach = function (func, thisp) {
		var i;
		var length = this.length;
		for (i=0; i<length; i += 1) {
		    if (this.hasOwnProperty(i)) {
			func.call(thisp, this[i], i, this);
		    }
	        }
	    };
	}

	// Wenn euch ein paar Array Funktionen hier im DOC begegnen,
	// die in etwa so geschrieben sind, dann habe ich die von Crock
	// genommen, um was raus zu lernen und euch die mal zu zeigen.
	

Damit kann man heute immernoch eine ganze Library schreiben. Auch wenn ich wenig vom Support für alte Browser und Versionen halte.

ES5 ist 2012 in allen Browsern drin. Es gibt keine Gründe, es nicht gleich im strict mode zu schreiben.

http://kangax.github.com/es5-compat-table/

Single Thread

Ich brauche mir keine Sorgen um konkurrierenden Zugriff in folgendem Fall machen. Darum kann es auch die define.module Variable oder aehnliches geben. Weil man sich keine Sorgen macht, dass jemand anders die ueberschreiben kann, solange diese Property in einer Sequenz gelesen und nicht mehr gebraucht wird.

Dennoch...nicht zu erwarten hat man, dass nach Beendigung der Funktion(en) der naechste Callback die nicht ueberschreiben kann. Das ist aber in unserem Fall nicht mehr noetig, denn der Single Thread hat uns vor konkurrierendem Zugriff bewahrt.

	// Modules/Async/A
	
	    var promise = Library.ensure(["a", "b", "c"], function (require) {
		var a = require("a");
		var b = require("b");
		var c = require("c");
	    });
	    
	// Jetzt, wie das gemacht wird.
	
	// Billig programmiert, indem ich meine Modules/Asynchronous Funktion,
	// die ich ebenfalls erst 1* geschrieben hatte (wenn auch komplett)
	// gepatcht habe. Mit dem ensuremode Flag sichere ich ab, dass ich nach
	// exports suche. Anderswo in dem Loader teste ich Code, ob es define
	// exports oder module.add enthaelt. Auch das ist ausbaufaehig.
	
	
	    // ensuremode bedeutete, dass ich jetzt exports lade.
	    // das beispiel war von einem Tag, als ich das Module
	    // noch nicht in den Module Constructor gehoben hatte,
	    // dass require und require.ensure kommunizieren konnten.
	    // (that flag had been replaced by a variable in the Module Constructor used as a Closure pattern to define require and ensure)
	
	    module.ensure = function (dependencies, callback) {
		module.require.ensuremode = true; // 1. Das Flag wird gesetzt.
		return module.require(dependencies, function () { // 2. Die Funktion gerufen
		    if (typeof callback === "function") {
			callback.call(this, module.require);
		    }
		});
	    };
	    
	    // jetzt ist das so mit dem flag, in einer besseren Fassung der Funktion
	    // entfaellt das flag (das brauche ich nicht mehr, es steht dann als var im Module Konstruktor mit den Definitionen
	    // und nutzt das Closure Pattern, da braucht man keine Flags)
	    // aber mit dem Thread kann man sicher sein, dass auch define.module nicht
	    // mehrfach beschrieben wird und so weiter.
	
	    module.require = function () {
		var ensure = !!module.require.ensuremode; // 3. Das Flag gelesen
		module.require.ensuremode = null; // 4. Und wieder geloescht.
		var deferred = Loader.defer();
		// ... jetzt der Rest der Funktion
		// eine, zwei Bildschirmseiten und 
		return deferred.promise();
	    };
	    
	    // Im Single Thread von JavaScript kann diese Reihe *NICHT* unterbrochen 
	    // werden. Ich denke mal, das ist so wichtig, es zu Beginn zu sagen.
	    
	

Hier nochmal die verbesserte Variante, weil das Closure Pattern das ermoeglicht. Ihr muesst weiter unten lesen, dass wir uns IN function Module () befinden.


function Module () {
// [...]

	// Danke des Singlethreads kann auch bei mehreren Require.ensure Calls
	// des selben Modules _nichts_ passieren.
	
	var ensuremode; 	    
	
        module.ensure = function (dependencies, callback) {
    	    var module = this;
            var promise;
    	    // module._ensuremode = true; // <- Aus dem Kapitel Single Thread das besagte Flag
	    ensuremode = true;
            promise = module.require(dependencies, function () {
                callback.call(this, module.require);
            });
            ensuremode = undefined;
            // if (!delete module._ensuremode) module._ensuremode=undefined;
            return promise;
        };
        
        var _require = function () {
            var args = arguments,
                dependencies = args[0],
                staticRequires,
                originalDeps, 
                factory = args[1],
                complete = false,
                xhr, url, evaluate, ensure, parent, promise;
		this.factory = factory;
                var id;
		var requireCall;
            
    		// ensure = !!module._ensuremode; 
        	// module._ensuremode = undefined;
        	ensure = !!ensuremode;
        	ensuremode = undefined;

	// [...]
	};
        	
}
	    

Dieser Vorgang kann dank des Single Threads _nicht_ von jemand anders unterbrochen werden.

Aber: Callbacks koennen das, immer am Ende einer Sequenz wird geguckt, ob noch was im Queue ist, sollte das der Fall sein, kommt das dran, das kann dan der naechste Callback sein. Dann kommt der naechste Callback, oder weitere Funktionen, die sich aus dem Ablauf ergeben.

		// Aus requireAsync: Das Promise muss nun anders sein, oder?
		// Bei Modules/Asynchronous function (m1, m2, m3, ...)
		// Und bei Modules/Async/A function (require), oder?
	    
		// Dank des Single Thread konnte ich ein Flag setzen.
		
		if (!ensure) {
		    deferred.resolve.apply(undefined, modules); // function (m1, m2, m3, ...)
		} else {
		    deferred.resolve.call(undefined, module.requireSync); // function (require)
		}
		
		// Hier kann ich das nutzen, in dem ich das Flag nutze.
	    
		// Haette ich mir Sorgen um Konkurrenz machen muessen,
		// haette ich mehr Code gebraucht, um den Zugriff speziell
		// zu sichern.
	    

Mit JavaScript geht heute zwar auch multithreading, ist aber im urspruenglichen Modell nicht vorgesehen und die Programmierer von heute verlassen sich darauf. Und man hat mir das selbst schon durch den einen (Stop writing spaghetti code) oder anderen Talk nahegelegt. Glaube ich zumindest so übersetzt zu haben. Und anderswo gehoert zu haben, dass man sich um die Concurrency eben keine Sorgen machen muss.

Die Funktion lauft somit stabil, ohne dass mein ensure Flag gekillt werden kann.

Aber

Was aber nicht gemacht werden sollte ist, sich bei Callbacks drauf zu verlassen, dass man solche Flags setzen kann. Wenn ihr den Code oben liest, wird das alles in einer Sequenz ausgeführt.

Wenn dazwischen jetzt Callbacks laufen und einer ein solches Flag will, kann jede andere Funktion, die davor läuft das manipulieren. Object.freeze kann eine manipulationssichere Variable raus machen, aber ich denke mal, dass der Use Case seltener dran kommt, dass eine Property da sein muss.

In JavaScript ist die Closure besser für den State und die Constructor Closure mit den Static Properties hat eine wunderbare Allmacht, man kan lokale Variablen erzeugen. Und anders als in ES3, wo der prototype.x als x auftauchen kann, geht das in ES5 strict auf keinen Fall mehr. Anderseits, wer hat da schon zwei gleiche Namen. Das Closure Pattern ist also wunderbar geeignet, um offene Flags zu vermeiden und intern mit lokalen Variablen zu arbeiten.

Präzedenz?

Weil das gerade so herrlich dumm war, ein ensuremode Flag zu erfinden, hier nochmal was anderes. An der Ausgabe von "Zuerst" und "Zuletzt" kann man erkennen, dass der Thread zuerst die Sequenz beendet und dann die in die Warteschlange des Event-Loops eingereihten Funktionen abarbeitet.

Sequenz (Thread) > process.nextTick (node.js) > setImmediate > setInterval > setTimeout

setTimeout(function () { console.log("timeout"); }, 0); 
var iv = setInterval(function () { console.log("interval"); clearInterval(iv); }, 0); 
console.log("Zuerst");
setImmediate(function () { console.log("immediate"); });
console.log("Zuletzt");
process.nextTick(function () { console.log("Erscheine noch vor setImmediate."); }); // auf node.js

// Zuerst
// Zuletzt
// Erscheine noch vor setImmediate.
// immediate
// interval
// timeout

Continuation

Der Continuation Passing Style wird im JavaScript durchgehend eingesetzt um sich von Callback zu Callback durch den Eventloop zu bewegen, ohne dass das Programm aufhoert zu laufen, oder, dass es wieder aufgeweckt wird, wenn das Ergebnis zur Verfügung steht.

		function continue(f) {
		    return f();  // sogenannter Tail-Call, eine Funktion anstelle des return Codes.
		}
		
		var data =
		continue(function () {			// Dank des Singlethreads werden diese
		    return continue(function () {		// da die keine asynchronen Callbacks sind
			return continue(function () {		// in einer Sequenz ausgefuehrt
			    return "Hallo Leute";	// und liefern dieses Ergebnis sofort zurueck
			});
		    });
		});
		console.log(data);
	    

Wenn man JavaScript programmiert gibt man dank der Lamdanatur von JavaScript staendig eine Funktion an die andere, die dann gerufen wird, wenn eventuell was geschieht, oder eventuell was schiefgeht.

		
		// ganz bekannte Form der Continuation
		
		xhr.onload = function (e) {
		    callback(e.responseText);
		};
	    
		xhr.onerror = function (e) {
		    throw new Error("xhr.onerror");
		}
	    
	    

Eine andere Form der Continuation ist entsprechend diesem Actor Model und allem was ueberhaupt "Messages" schickt, die Rueckmeldung, zum Beispiel aus dem Vat.

	    
		// Der Worker fuehrt unabhaengig Code auf Messages aus und schickt
		// dann vorzugsweise eine Message zurueck oder schweigt.
	    
		// worker.js
		function compute (data) {
		    return "<h1>Computed "+data+">/h1>";
		}
		// ...compute
		self.onmessage = function (e) {
		    if (e.origin === "/root") {
		        compute(e.data);
		    }
		}
		self.postMessage(compute(, "/worker1/view");
		
		
		// window.js
		// Hier wird das im Vat erzeugte Zeugt angenommen und mediiert
		
		var vessel = new Worker("worker.js");
		vessel.onmessage = function (e) {
		    if (e.origin === "/worker1/view") {
			var div = document.createElement("div");
	    		div.innerHTML = e.data;
			document.body.appendChild(div);
		    }
		};

	    
	    

Da wird kontinuiert, wann immer eine Message geschickt und erhalten wird. Wenn jemand die zu nutzen weiss, kann er weiterarbeiten. Das ist auch eine Form der Programmierung (hier in JavaScript) an die man sich halten muss.

First Class

First Class Functions ist wohl der Fachbegriff aus dem Sprachdesign. JavaScript functions sind First Class Values

Funktionen sind also erstklassige Werte, Function-Objekte in JavaScript. Anders als in Java und C++ oder anderswo, koennen sie praktisch überall im Code eingesetzt werden. Überall, wo ein Wert oder ein Ausdruck, erwartet wird.

Das verleiht JavaScript eine unglaubliche Dynamik gegenüber anderen Programmiersprachen. Diese Dynamik kennt man nur von Scheme oder Lisp, wo diese ueberall-einsetzbaren Funktionen Lambda genannt werden. function in JavaScript und lambda in Lisp sind quasi das gleiche.

Das folgende Beispiel zeigt einfach ein paar Funktionen durcheinander, was in anderen Sprachen bereits nicht gehen wuerde, ohne tieferen Sinn.

	    var f = function(a, b) {
		var c = a.call(a, 1,2,3,4),	
		d = b.apply(b, [1,2,3,4]); 
		return function () {
		    return {
			a: c,
			b: d
		    };
		}
	    };
	    
	    var g = function () {
		var a = arguments[0],
		b = arguments[1],
		c = arguments[2],
		d = arguments[3];
		return { 
		    "a": a,
		    "b": b,
		    "c": c,
		    "d": d
		};	
	    }; 
	    
	    var e = f(g, (function (f) {
		return function () {
		    console.dir(arguments); // zeigt die oben angegebenen 1,2,3,4 an.
		    return f;    // Gibt immer 4,5,6,7 zurueck, und nicht die Argumente von b.apply
		}		// die Argumente werden in der Funktion nicht verarbeitet 
	    }(g(4,5,6,7))));	// sondern f also [4,5,6,7] zurueckgegeben, 1,2,3,4 sind aber verfuegbar 
				// auf arguments in der function () { return f; } (das ist b)
	
	    console.dir( e() );
	    // Object
	    // 0: Object {a:1,b:2,c:3,d:4}
	    // 1: Object (a:4,b:5,c:6,d:7}
	
	

JavaScript hat ein zum Glück lockeres (und auch sicheres) Typensystem. In den anderen Sprachen würde das, was man hier machen kann, laufend zu Fehlermeldungen führen. Man kann naemlich fast alles fast ueberall einsetzen, so lange man ein paar Regeln beachtet.

Hier musste man dauernd zwischen Adresse und Wert schalten, Casten (Typumwandlung) und sich auch sonst das Leben "schwer" machen. In Wirklichkeit habe ich immer gerne C gemacht, aber verglichen mit dem Komfort beim JavaScript

Nicht-wissenschaftlicher Vergleich mit C

Hier in C sind Funktionen zum Beispiel nicht first Class. Und auch mit der Konvertierung von Typen wird es in der Regel sehr ernst zugehen. Man muss zwischen statischen Variablen und Adressen von Speicherbereichen, wo sich die Inhalte der Variable aufhalten hin- und herschalten. Da muss man dann mit .-Notation wie ->Notation wechseln, sowie zwischen Typen flexibel (*casten) können.

    // das geht ohne casting (gerade so) weil int und char sehr sehr aehnlich sind
    
	// C++
	#include <iostream>
	void main() 
	int i;
	char c;
	{
	    i = 65;
	    c = i; // Das muesste gehen
	    std::cout << c << endl; // gibt A aus (char code 65)
	}
	
    //  hier kommen Anfänger erstmal ins Grübeln.
	// C++
	#include <iostream>
	void test () {
	    std::cout << "point";
	}
	void (*func_ptr);
	void main() 
	{
	    func_ptr = test;
	}
	// error: invalid conversion from void (*)() to void*

    // so castet man den FALSCH		(kein Error, aber falsche Adresse)
    
	void main() 
	{
	    func_ptr = (void *)test;
	}
	// ok
	

Funktionszeiger sind uebrigens das Geheimniss der objektorientieren Programmierung in C (nicht C++), anstelle von Methoden in der struct hat man Funktionszeiger. Der Linux Kernel macht massiven Gebrauch davon.

func_ptr() kann man jetzt nicht so aufrufen. Denn ich habe da einen Fehler gemacht. Man braucht nicht zum Zeiger zu Casten. Das geht mit dem Adressoperator viel einfacher. Das geht so:

	#include <iostream>
	void test () {
	    std::cout << "point";
	}
	void (*func_ptr)(void);	// hier muss der parameter (void) mitangegeben werden.
	void main() 
	{
	    func_ptr = &test;	// & gibt die Adresse (&Adressoperator)
	    func_ptr();		// geht
	    (*func_ptr)();	// ebenfalls
	}
	

Ihr versteht, warum JavaScript da echt Spass machen kann? Firstclass

    // JavaScript
    function test() {
    }
        var func_ptr;
    
    (function main () {
	func_ptr = test;
	func_ptr();
    }());

Ausserdem noch erstklassig, oder? Genau so, wie man es haben will. Die Arbeit macht der Interpreter und man kann sich das staendige Umwandeln und Zuweisen von Funktionsadressen sparen.

"First Class Functions" ist der Fachbegriff, der bezeichnet, dass die Funktion an erster Stelle stehen kann, dort, wo ein Wert erwartet wird. Was bedeutet, dass man die Funktion fast überall einsetzen kann. In LISP und anderswo nennt man das Lambda. Da die Funktionen ueberall stehen koennen, ist JavaScript extrem flexibel. Das bedeutet nicht, dass man es erstklassig findet.

Aaah, ich glaube dadurch ist mir das oberste Beispiel eingefallen.

// von Douglas Crockford stammt das Beispiel und der Satz
// "Statically scoped first class functions with lexical closure"
// Beispiel: Der Y Combinator
var Y = function (le) {
    return (function (f) {
	return f(f);
    }(function (f) { return le(function (x) { return f(f)(x); }); }));
};

// Ich teste das mal. Ich habe keine Ahnung, was der Y Combinator in Wirklichkeit kann.
// Ich erinnere mich daran, angenommen, man "haette nur Funktionen als Werkzeug"; Aber kann das jetzt nicht aus dem Kopf
var Z = function(a) { return a; };
var H = function() { return 23; };

console.log(Y(Z));
// [Function]
console.log(Y(H));
// 23
console.log(Y(Y(Z));
// var Z = function(a) { return a; }
//                 ^
// RangeError: Maximum call stack size exceeded

Hihi, den Callstack überlaufen lassen habe ich. Hehe.

Wo ich das Beispiel jetzt verwende, muss ich bis demnaechst herausfinden, wovon ich rede. :)

Also der Y Combinator kann nur eines: Rekursion. Das ist eine Funktion, die es moeglich macht, ohne irgendwelche anderen Operatoren ausser der Funktion eine Rekursive Operation durchzufuehren. Was fuer den Zahlengenerator, den alle da nutzen fibonacci, reicht.

Hmm. Muss ich noch mehr drueber rausfinden.

The chapters are poorly sorted and seriously not in the right order.

Partial Application

Fast vergessen hatte ich, dass bind Partial Application bietet, wenn man also einige Parameter zusammen mit dem this binden will, kann man bind auch Argumente uebergeben.

    function f(a,b,c) {
	console.log(a);
	console.log(b);
	console.log(c);
    }
    
    var g = f.bind(null, "erstens", "zweitens"); // das this objekt null bedeutet normal das globale objekt, im strict mode aber wohl undefined
    g("drittens");
    // erstens
    // zweitens
    // drittens

Die Curry Funktion - Umwandlung einer Funktion mit mehreren Argumenten in eine Funktion mit einem Argument.

Hier unterliege ich zuerst seit langem wieder erstmal einem Irrtum, was Curry heisst. Ich dachte an "Würzen" einer Funktion, weil man bei zwei Argumenten auf eins reduziert hat und sich den zuerst gegebenen Wert merkt und eine Funktion zurueckgibt, die diesen Wert zusammen mit dem naechsten aufruft, so wurden intern zwei Parameter gehandelt, nach aussen aber nur einer...

Die Wahrheit über Currying kann man aber in der Wikipedia lesen.

var curryFunction1 = function ( value, callback ) {
    return function () {
	return callback.apply(null, [value].concat([].slice.call(arguments)));
    };
};

var increment = curryFunction1(5, function (a, b) {
    var result = a+b;
    console.log("curry value + function argument = " + result );
    return result;
});

increment(5);
// curry value + function argument = 10

Hier ist meine Fortsetzung der Funktion, dass man sie immer wieder mit dem letzten Wert aufrufen kann. In diesem Fall muss man sie manuell chainen.

var curryFunction2 = function ( value, callback ) {
    var lastresult = value;
    var curry = function () {
	lastresult = callback.apply(null, [lastresult].concat([].slice.call(arguments)));
	return curry;
    };
    return curry;
};

var increment2 = curryFunction2(5, function (a, b) {
    var result = a+b;
    console.log("curry value + function argument = " + result );
    return result;
});

increment2(1)(2)(3)(4)(5)(6);
// curry value + function argument = 6
// curry value + function argument = 8
// curry value + function argument = 11
// curry value + function argument = 15
// curry value + function argument = 20
// curry value + function argument = 26

Wenn ich sie ganz statisch machen moechte muss ich return curry; mit increment2 = curry; ersetzen und sie immer wieder überschreiben.

Wieviel das jetzt mit der Originaldefinition von Schoenfinkeln und Frege oder von Curry zu tun hat, muss ich noch herausarbeiten.

uncurrying

Bei Axel Rauschmayer (http://2ality.com/2011/11/uncurrying-this.html) wurde ich auf das uncurrying aufmerksam. Er sagt, das hiesse soviel wie obj.foo(arg1, arg2) in foo(obj1, arg1, arg2) zu transformieren.

Ich will das jetzt nicht einfach abschreiben. Darum habe ich die Seite bereits verlinkt.

    // Ich habe jetzt mit Absicht was eigenes erfunden, was auch uncurried,
    // mehr Beispiele und Function.prototype.uncurryThis findet ihr beim Axel

    var obj = {
	foo: function (arg1, arg2) {
	    this[0] = arg1;
	    this[1] = arg2;
	}
    };
    
    var uncurry = function (f) {
	    return function () {
		f.apply(arguments[0], [].slice.call(arguments, 1)); 
	    };
    };
    var arr = ["x","y"];
    
    var foo = uncurry(obj.foo);
    foo(arr, "a", "b");
    console.log(arr[0], arr[1]);
    // a b
Weiterführendes

Die volle Lösung und Unterscheidung von Currying und Partial Application, ich denke wohl auch wohl die Quelle, von der ich das lernen werde, ich glaube Crockford gab mir die Uebersicht fuer Curry und Partial, und hier geht es weiter kann man hier lesen: http://2ality.com/2011/09/currying-vs-part-eval.html im Blog vom Rauschmayer, der weiss das natürlich und kennt sich mit EcmaScript aus..

Manchmal ist das was ich schreibe nicht zusammengehoerig. Ich habe gerade eine Stelle entfernt, Junge, Junge.

Function

First Class Values

Und uebrigens selbst Objekte. Man kann z.B. f.property = 12; schreiben.

	// Funktionen koennen Properties wie jedes andere Objekt haben
	// Das ist in JavaScript so, aber nicht in C. Das ist was besonderes.
    
	function f() {
	    f.random = 12;
	    return f.property;
	}
	f.property = 42;
	console.log(f.random);
	// undefined
	console.log(f());
	// 42
	console.log(f.random);
	// 12
    

new Calls with a Fresh this Object to write on.

	var fresh = new Function("return 'yeah';");
	var fresher = function () { return "yeah"; };
	var oldObject = { a:1 };
	var freshest = Object.create(oldObject);
	freshest.a  = 2;
	console.log(freshest.a);
	// 2 [freshest shadows a:1 von oldObject, nur arrays und objekte sind shared]
	delete freshest.a;
	// true
	console.log(freshest.a);
	// 1 [von oldObject]
    

Only Object.create (binary) and new plus [], {}, function, "", /regex/, a 12 number Create Fresh JSObjects. Glaube ich zumindest. Alles andere, was bei = bei rumkommt sind Referenzen auf das existierende Objekt.

Funktionsparameter kommen unveraendert aus Funktionen raus, nur kann man Objekten zum Beispiel ihre Properties aendern. Man kann die Keys der Objekte aendern, die reingegeben werden. Man kann das Objekt nicht austauschen, aber aendern. Es ist quasi eine Mischung aus Referenz und Call by Value.

	var x = 12, y = {}, z = [];
	function test (v) {
	    if (v === 12) { return (v = 13); }	/
	    else if (typeof v === "object") {
		v.key = "value"; // Assignment ok
		v = {};	// Ersetzen ist unmoeglich
		    // Ausserhalb der Klammer ist sogar key gesetzt.fu
	    } else if (Array.isArray(v)) {
		v.push("cool");
		v = []; // Ersetzen ist unmoeglich, 
		    // innerhalb der Funktion ist die Variable geloescht,
		    // aber ausserhalb nicht
	    }
	}
	console.log(test(x));
	console.log(test(y));
	console.log(test(z));
	
	console.log(x);
	// 12 x ist 12	-- unveraendert
	console.dir(y);
	// { key: "value" } y hat key aufgenommen, v = {} war hingegen wertlos
	console.dir(z);
	// z hat cool aufgenommen
    

Oder wisst ihr noch was. Hier komment fette Sachen von den Module Patterns und Objekt Patterns vom Crockford raus, mit den kann ich noch ein paar Kapitel dienen.

	// Ein FunctionStatement (kein Semikolon)
	function Statement () {
	    
	}
	
	// Eine function Expression (right hand side von der var als Teil der Expression var exp = fu)
	// (Semikolon)
	var expression = function () {
	
	};
	
	// Ein FunctionStatement mit einem this 
	// Diese Funktion wird mit new aufgerufen
	// Nur das grosse C von Constructor deutet das an.
	// Sonst nichts.
	function Constructor (name) {
	    this.name = "Edward" + name ? " "+name : "";
	    console.log("Hi, my name is " + this.name);
	}
	Constructor.prototype = {
	    constructor: Constructor,
	    shared_property: [],
	    shared_but_shareable_functions: function () {
		[].slice.call(arguments).forEach(function (arg) {
		    // arg = arguments[ 0..n ]
		    console.log(arg);
		});
	    }
	};	
	var obj1 = new Constructor();
	var obj2 = new Constructor("Gerhold");
	obj1.shared_property.push("Yes");
	obj2.shared_property.push("No");
	var obj3 = new Constructor("WAT");
	obj3.shared_property.forEach(function (p) { console.log(p); });
	// Yes
	// No
	
	
	// Ein idiotensicherer Konstruktor mit ohne-new und arguments von mir
	// Hier ist es egal, ob man "this" weglaesst.
	function IdiotSafeConstructor () {
	    var construct = this;
	    if (!this instanceof IdiotSafeConstructor) { 
		construct = new IdiotSafeConstructor();
	    } else {
		// construct.name = "Edward"
		Object.defineProperty(construct, "name", { value: "Edward", writeable: false });
	    }
	    // Mit IdiotSafe Arguments after construct?this?if-else
	    if (arguments.length > 0) {
		// construct.* -
		Object.defineProperty(construct, "lastName", { value: arguments[0] });
	    }
	    return construct;
	}
	
	var idiot1 = IdiotSafeConstructor();
	var idiot2 = new IdiotSafeConstructor("Gerhold");
    

Es gibt noch mehr functions. Als First Class Values.

	// Returning a function, Funktion zurueckgeben
	    function give_back(obj, name) {
		// POLA
		return function giving_you_that () {
		    // Nur that
		    return obj[name];
		};
	    }
	    
	// Passing a function. Eine Funktion uebergeben
	    function continue_with (func) {
		"use strict";
		func(); // laeuft im global scope oder mit this === undefined im strict mode
	    }

	// Ein sogenannter Callback
	    function continue_with_me () {
		// ein Callback
		console.log("working with " + this.toString());
	    }
	    
	// Erzeuge on the Fly Funktion an einem Objekt und gebe der Funktion eine Funktionen
	    var obj = {};
	    obj.continue_with = continue_with.bind(obj);
	    obj.continue_with(continue_with_me);
    

Und das ist noch nicht alles. Das Kapitel Functions als First Class Values ist sehr lang.

Power Constructors

Douglas Crockford hat den Begriff Power Constructor glaube ich erfunden.

Er hat einen Power Constructor etwas anders definiert. Ich zeige jetzt einen Constructor der all diese Power des Power Constructors und noch mehr von dem Programm hat.

Damit meine Ich Konstruktoren wie meinen alten Module Konstruktor.

Das meine Ich mit Power Constructor. Auf die Art und Weise kann man auch so dämliche Flags, wie in dem Single Thread Beispiel, was hier zuerst drin ist unter Kontrolle kriegen. Die gehoeren alle in so einen Konstruktor. Und gute Objekte koennen noch mehr. Das ist gar nicht so gut. Der Prototype hier drunter kann ebenfalls eliminiert werden. WER BRAUCHT DEN SCHON? DEN EINEN PROTOTYPE? Wollen wir den paar Modulen ein paar Bytes sparen und einen Protype extra erzeugen und wieder ein paar Bytes dafuer allozieren, aber nur einmal, oder wollen wir noch bessere "Power Konstructoren?"

Noch bessere Power Konstruktoren. Und ihr habt recht. Auch das hier muss ich fortführen, um erstmal was raus zu machen.

    
    function Module () {

	var module = this;
        var arg0 = arguments[0];
        var arg1 = arguments[1];

	var requireCallId; // single thread, aber asynchrones loading...concurrency...unpredictable order
	var ensureMode;  // require.ensure nutzt in meiner version require mit einem flag, eine extra routine in require zu nutzen
	
	this.sandbox = {};
        /* umstritten (mit mir) */
        this.module = { exports: {} }; // I do not pass the big module, just a proxy. Oder? Object.defineProperties (readonly)
        /* lieber const module.id, etc */
        
    /* Einige dieser FLAGS sollten wieder in die Closure zurueck.... */
        this.plugin = null,
        this.id = null;
        this.filename = null;
        this.exports = {};
        this.parent = null;
        this.children = [];
        this.dependencies = [];  	   // <--- als Objekte, nicht als Strings
	this.localrequireCallIds = [];	   // keeps arguments for requieres run in this contexts // falls es die require factory is
        this.loaded = false;
        this.text = "";
        this.executedFactory = false;
        this.factory = null;	   // falls es ein modul mit factory is	(define, nicht require);
        this.evaluated = false;
        this.type =  null;

        if (!this instanceof Module) {
	    throw new TypeError("Module: New keyword for the constructor function expected.");	
	}

	module._randomid = Math.random();
	// default event listeners
        module.define = function () {
    	    return __define.apply(module,arguments);
        };	
        
        module.define.module = this;
        module.define.amd = {
            conformsToday: false,
            butWorksABit: true,
            multiversion: false,
            jQuery: false,
            jQueryLater: true,
            backbone: "later, too"
        };
        
        module.define.package = function () {};
        module.ensure = function (dependencies, callback) {
    	    var module = this;
            var promise;
	    ensureMode = true;
            promise = module.require(dependencies, function () {
                callback.call(this, module.require);
            });
            ensureMode = false;
            return promise;
        };
        
        var __require = function () {
            var args = arguments,
                dependencies = args[0],
                staticRequires,
                originalDeps, 
                factory = args[1],
                complete = false,
                xhr, url, evaluate, ensure, parent, promise;
		this.factory = factory;
                var id;
                var requireCallId;
            
            ensure = !!ensureMode;
	    
	    if (isObject(args[0])) {
		// config object;
	    }
	    
            var deferred = Loader.defer();
            if (!dependencies) {
                return;
            }
            
            if (typeof dependencies === "string") {

		id = this.require.toUrl(dependencies);
                dependencies = cache.get(id);
                if (dependencies && dependencies.exports) {
		    return dependencies.exports;
                } else {
            	    throw new Error("require(id): Can not resolve "+id);
                }
                
            } else if (Array.isArray(dependencies)) {

                originalDeps = [].concat(dependencies);
                staticRequires = module.findStaticRequires(factory.toString());
                if (staticRequires.length > 0) {
                        staticRequires = staticRequires.filter(function (id) {
                    	    return dependencies.indexOf(id) === -1;
                        });
                        dependencies = dependencies.concat(staticRequires);
                }

		requireCallId = module.localrequireCallIds.length;
		var require_call =  {
		    id: requireCallId,
		    context: module,
            	    dependencies: dependencies,
            	    factory: factory
		};
                module.localrequireCallIds.push( require_call );

		var children = dependencies.map(function (child_id) {
		    var child;
		    var fullname = module.toUrl(child_id);
                    if (!cache[fullname]) {
                	child = new Module(child_id, module);
                    	child.load();		
                    } else if (cache[fullname]) {
                	child = cache[fullname];
                    }
                    return child;
                }); 
		children.forEach(function (child) {
		    if (module.children.indexOf(child) === -1) module.children.push(child);
		});
                require_call.children = children;
            }
            return deferred.promise();
        };
        
        module.require = function () {
    	    return __require.apply(module,arguments);
        };
        
        module.require.toUrl = function (id) {
	    return module.toUrl(id);
        };

        var __requireSync = function (id) {
    	    var parent = this;
	    var module = new Module(id, parent);
	    module.loadSync(); 
        };

        var __requireSyncError = function (id) {
    	    throw new Error("Sorry, Module "+id+" is not in the Cache.");
        };
        
        module.requireSync = function (id) {
    	    if (cache.has(id)) {
    		return cache.get(id);
    	    } else {
	        return __requireSync.apply(this, arguments);
    	    }
        };
        
        module.plugins = {
    	    "lib" : function (module, code) {
    		var fn = new Function("Library", code);
    		var ret = fn.apply(module.sandbox, [Loader]);
    		return ret;
    	    },
    	    "css" : function (module, code) {
    		module.exports = code;
    	    },
    	    "text" : function (module, code) {
    	    }
        };

	module.findStaticRequires = function (code) {
	    var dependencies;
	    var dependencies = [],
                match;
            do {
                match = regex.dependenciesExpr.exec(code);
                if (match && match[1]) {
                    dependencies.push(match[1]);
                }
            } while (match);
            return dependencies;
	};

	// text!, css!, all!
	module.getPluginPart = function (id) {
	    return id === null ? null : id.substr(0, id.indexOf("!"));
	};

	module.getIdPart = function (id) {
	    if (id === null) return null;
    	    if (id.indexOf("./") === 0) id = id.substr(2);
	    return id === null ? null : id.replace(module.getPluginPart(id)+"!", "");
	};

	module.toUrl = function (id) { // filename
    	// a.k.a require to Url
	    var basedir = "", 
	    l, 
	    id = id || module.id,
	    ext = "";
	    if (id === null) {
		if (arguments[0] === undefined) module.uid = null;
		return null; // for global context
	    }
	    id = module.getIdPart(id); // plugin muss vorher weg;
	    ext = baseext(id);
	    if (!ext) ext = ".js" 
	    else ext = "";
    	    if (module.parent !== null) {
    		basedir = basepath(module.parent.toUrl());
    		l = basedir.length;
    	    }
    	    if (l > 0 && basedir[l-1] !== "/" && !/[\/]/.test(id[0])) basedir += "/";
	    if (!arguments[0] === undefined) module.uid = basedir + id + ext;
    	    return basedir + id + ext;
        };
        
	module.evaluate = function (code) {
	    var plugin = module.plugin;
            if (!module.evaluated) {
    		if (code) module.text = ""+code;
    		if (!plugin) {
    		    var ret;
    		    module.evalFn = new Function("require", "exports", "module", "define", "Loader", module.text);
        	    ret = module.evalFn.apply(module.sandbox, [module.require, module.exports, module, module.define, Loader]);
        	    module.evalReturnCode = ret;
        	    module.evaluated = true;
        	    return ret;
        	} else {
        	    var fn = module.plugins[plugin];
        	    if (isFunction(fn)) {
        		ret = module.evalFn(module, code);
        		module.evaluated = true;
        		return ret;
        	    }
        	}
            } else {
        	Loader.debug(module.id + "already evaluated");
            }
        };

	module.loadSync = function () {
	    if (!module.loaded) {
	    requestSync("GET", module.toUrl(), null).then(
		function (err, data, headers) {
		    if (data) {
		    module.loaded = true;
		    module.text = ""+data;
		    module.evaluate();
		    module.run();
		    } else {
		    module.error = true;
		    }
		}
	    );
	    }
	    if (!module.error && module.evaluated) {
		return module.exports;
	    } else {
		throw new Error("Sorry, can not resolve module "+module.id);
	    }
	},

	module.load = function (requireCallId) {
    	    var reqs = requireCallId !== undefined ? module.localrequireCallIds[requireCallId] : module;
    	    if (!module.loaded) {
    	    var xhr, fs;
    	    var module = module;
    	    //var deferred = Loader.defer(module);
	    var callback = function (err, data, headers) {
		Loader.debug("callback!");
    		if (!err) {
		    module.loaded = true;
    		    module.text = ""+data;
    	    	    module.evaluate();
    	    	    // module.runIfMyModulesAreComplete()
    	    	    if (module.parent && !module.parent.executedFactory) module.parent.runIfMyModulesAreComplete();
    		    //deferred.resolve(err, data, headers);
    		    //module.emit("load");
    		} else { 
    		    module.error = true;
    		    //deferred.reject(err, data, headers);
    		    //module.emit("error");
    		}
	    };
    	    if (isBrowser || isWorker) {
    		request("GET", module.toUrl(), null, null).then(callback, callback);
    	    } else if (isNode) {
    		fs = globalRequire("fs");
    		fs.readFile(module.toUrl(), callback);
    	    }
    	    //return deferred.promise();
    	    }
        };

	module.cache = function (id) {
    	    id = id || module.toUrl();
    	    if (!module.cached && id !== null) {
    		cache[id] = module;
    		module.cached = true;
    	    // console.log("cached module: "+id);
    	    }
    	    
        };
        
	module.uncache = function (id) {
    	    id = id || module.toUrl();
            // console.log("uncache");
    	    if (module.cached) {
    		if (cache[id]) {
    		    cache[id] = null;
    		}
    		module.cached = false;
    	    }
        };
        
    
	module.declare = function (factory, args) {
	    module.factory = factory;
	    module.args = args || module.args;
	    var exports = factory.apply(module.sandbox, args);
            if (typeof exports !== "undefined") module.exports = exports;
            module.executedFactory = true;
            module.evaluated = true;
            module.loaded = true;
            return module.exports;
	};
    
	
	// Ok ---
	/*
	
	    Die hier kamen aus dem Prototype.
	    Jetzt mache ich das module. weg
	    unf eine function isComplete etc.
	    raus, die im Module Constructor ist
	    
	    Dann habe ich sowohl einen Power
	    Constructor, als auch ein bombensicheres
	    AMD Module.
	    
	*/
	
	module.isComplete = function () {
	    Loader.debug("is "+module.toUrl()+" complete? "+module.evaluated);
	    return module.evaluated;
	};
	
	module.areMyChildrenComplete = function (requireCallId) {
	    var req = requireCallId ? module.localrequireCallIds[requireCallId] : module;
	    return req.children.every(function (child) {
		return child.areMyChildrenComplete();
	    }) && module.isComplete(); 
	};
		
	module.runIfMyModulesAreComplete = function (requireCallId) {
	    var req = requireCallId ? module.localrequireCallIds[requireCallId] : module;;
	    var ac;
	    if (module.areMyChildrenComplete(requireCallId)) {
		module.runFactory(requireCallId);
	    }
	};
	
	module.getArguments = function (requireCallId) { // for factory arguments
    	    var req = requireCallId ? module.localrequireCallIds[requireCallId] : module;
            return req.children.map(function (m) { return m.exports ;});
        };
	
	module.runFactory = function (requireCallId) {	// diese factory (erzeugt exports) oder die des require calls (callt mit allen exports)
	    var req = requireCallId ? module.localrequireCallIds[requireCallId] : module;
	    if (typeof req.factory === "function" && !req.executedFactory) {
	    //    Loader.debug("*** run "+module.toUrl()+" - "+requireCallId);
	    	req.args = req.args || module.getArguments(requireCallId);
		req.factory.apply(module.sandbox, req.args);
		req.executedFactory = true;
	    }
	};
	
	/*
	* Construction Code
	*/
	
	if (isObject(arg0) && arg0 !== null) {
            Object.keys(arg0).forEach(function (key) {
                module[key] = arg0[key];
            });
        } else if (arg0 === null || typeof arg0 === "string") {
            module.id = arg0;
        } else {
	    module.id = null;
	    Object.defineProperty(module, "id", {
		writeable: false
	    });
        }
	if (isObject(arg1)) {
	    module.parent = arg1;
	}
	module._id = module.id;
	module.plugin = module.getPluginPart(module._id);
	module.id = module.getIdPart(module._id);
	
	if (this.id !== null) {
	    // module mit id
	    if (!this.filename) this.filename = this.toUrl(); 
	} else {
	    // main context module ohne id
	    this.loaded = true;
	    this.evaluated = true;
	    this.executedFactory = true;
	    this.completed = true;
	}
	
	if (this.id) this.cache();
        return module;
    }
    

Zu function __define geht es hier (das ist eine AMD define kompatible Funktion).

Verbesserung

Ich habe den Modulkonstruktor innerhalb von zehn Minuten verbessert gehabt. Vorher hatte er einen Prototype. Jetzt muessen die this.variablen, die nicht public sichtbar sein sollen noch in die Closure zurueck.

    
	if (this.id) this.cache();
        return module;
    };
    
    Module.prototype = {
        constructor: Module,
    //    __proto__: new Emitter(),

	findStaticRequires: function (code) {
	    var dependencies;
	    var dependencies = [],
                match;
    

Das ist ein grosser Teil von Require. Ein vernuenftiges Modul, was als Root auch als Globales Kontext Modul laufen kann. Man kann da aber auch eine spezielle globale Instanz haben, wie den Module Loader, oder die JavaScript API. Das ist ganz das, was ihr wollt. Und fertig und fuer richtige Production kann man sowas wie requireJS nehmen, was von J.Burke geschrieben ist, und ein fertiges AMD System ist.

Der Prototype ist shared

Wenn man mehrere Objekte mit dem gleichen Prototype hat, haben sie nicht jeder einen Prototype, sondern alle einen Prototype.

    
	var derPrototyp = {
	    geteilt1: [],
	    geteilt2: {},
	    geteilt3: function (str) {
		console.log("Hallo "+str);
	    }
	};
	
	// Objekt.create
	var dasObjekt1 = Object.create(derPrototyp, {
	    // das ist eine Propertydescriptormap, kein Objektliteral,
	    // die wird so ebenfalls bei Object.defineProperties verwendet.
	    "eineEigenschaft" : {
		value: "meinWert"
	    },
	    "einGetter": {
		get: function () {
		    return this.eineEigentschaft;
		}
	    },
	    "einSetter": {
		set: function (v) {
		    this.eineEigenschaft = v;
		}
	    }
	});
	
	console.log(dasObjekt1.einGetter); // meinWert
	
	// __proto__
	var dasObjekt2 = {
	    "ich": "hab auch properties",
	    "und_zwar": function () { return this.ich; },
	    __proto__: derPrototyp
	};
	
	// new Constructor mit prototype Property
	var dasObjekt3 = (function () {
	    function Objekt3 () {
	    }
	    Objekt3.prototype = derPrototyp;
	    Objekt3.prototype.ausserdem = function () { return "einzeln hinzufuegen wenn man eine prototype property davor mit einem Objekt setzt."; };
	    return new Objekt3; 
	}());
	
    

Gut und jetzt zeigen wir was, was man wissen sollte, bevor man dem Prototype einen Array EventListeners[] zuweist und nicht guckt, ob jeder einen eigenen Array hat.

	// Beispiel Array
    
	dasObjekt1.geteilt1.push("vonObjekt1");
	dasObjekt2.geteilt1.push("vonObjekt2");
	dasObjekt3.geteilt1.push("vonObjekt3");	
    
	// Schauen wir uns das an.
	// dasObjekt1.geteilt1.forEach(console.log); TypeError: Illegal Invocation mit console.log als callback
	dasObjekt1.geteilt1.forEach(function (k) { console.log(k); });
	// vonObjekt1
	// vonObjekt2
	// vonObjekt3
	
	// Beispiel Objekt
	
	dasObjekt1.geteilt2["obj1"] = "vonObjekt1";
	dasObjekt2.geteilt2["obj2"] = "vonObjekt2";
	dasObjekt3.geteilt2["obj3"] = "vonObjekt3";	
	
	// Schauen wir uns das an
	Object.keys(dasObjekt1.geteilt2).forEach(function (k) { console.log(k); });
	// obj1
	// obj2
	// obj3
	
	// Methoden auf einem Prototype hingegen zu haben ist brilliant.
	dasObjekt1.geteilt3("Objekt1");
	dasObjekt2.geteilt3("Objekt2");
    	dasObjekt2.geteilt3("Objekt3");
    

Das Prototype Pattern eignet sich damit aber sicherlich super fuer einen shared bus, aber.

Prototype mal insecure

Wenn ich ein Objekt mit dem gleichen Prototype habe, kann ich den Prototype aller Objekte mit dem gleichen Prototype beeinflussen.

	// oder man kann damit Libraries wie z.B. MooTools schreiben, die hunderte native Prototype Extensions bietet
	// oder man ergaenzt sein DOM
	

	HTMLPreElement.prototype.hightlight = 
	HTMLCodeElement.prototype.highlight = (function () {
	    // Die Daten in der Closure
	    var table = {};
	    table.js = [
		[/(var|let|const)/, "var-class"] // kv[0] und kv[1] @ source.replace
	    ];
	    table.cpp = [
		[/void/, "void-class"] 
	    ];
	    // Die Funktion auf den Prototypen
	    return function (language) {
		var data = table[language || "js"];
		var source = this.innerHTML;
		data.forEach(function (kv) {
    		    source.replace(kv[0], function (match0) {
    			return "<span class='"+kv[1]+"'>"+match0+"</span>";
    		    });
		}); 
		this.innerHTML = source;
		return this; // chaining
	    }
	}());
    

Man kann das Dom ergaenzen, anderseits ist der gesharte Prototype ein superschweres Ding in Sachen Sicherheit.

Das ist ein superschweres Ding fuer Schaedlinge aller Art, weil es leicht ist, in nicht zum Beispiel mit Object.freeze oder den Data Propertiers writeable: false, configurable: false geschützte Objekte über den Prototype einzudringen. Und wenn der geshared wird, weil man die Methoden braucht, kann man sicher sein, das einer irgendwann mal die Funktion ausfuehrt.

Daraum sollte man ein Auge haben, wenn man seine Prototypes schreibt.

Funktionale Programmierung

In JavaScript kann man besser mit Funktionen, als strikt objektorientiert programmieren. Objekte gehoeren dazu, die gebe ich weiter, die nehme ich an, die verarbeite ich und bearbeite ich, ich zeuge Objekte. Ich gebe sie dem GC frei, dass er die auf dem Heap markieren kann, und dann nach dem Scan sweepen kann. Objekte sind wichtig!

Aber gerade die function macht JavaScript zu einem Interpreterphaenomen, das LISP Freaks bereits kennen. Auch ich wolllte mal emacs oder nyquist programmieren. Letzteres waere richtig geil, weil man damit in audacity Musik erzeugen und den Wavetrack bearbeiten kann, ich habe das vor drei Jahren beim Hiphop machen mal ausprobiert, bin aber nicht lange beim programmieren geblieben.

    
    (function () {
    
	/+ Das ist meine AMD define Version aus 
	der Beta Version meines allerersten Loaders...
	Ich habe in Module soviele idiotische public Properties
	drin, die in die Closure als unsichtbare Variable gehoeren
	(erstmal musste ich module.prototye noch als this.props in
	den Konstruktor heben, damit ich das machen kann, aber jetzt..)
	dass ich diese Funktionen gewiss irgendwann nochmal updaten 
	werde. Aber zum Verstaendnis von require und define und der
	grossen CommonJS Module Struktur sollte das reichen, ich habe
	ein paar Tage probiert, bis ich die optimale Richtung hatte,
	was ein Module denn jetzt sein soll. Und glaubt nicht, dass mit
	ES6 gleich alle Loader platt sind.
	*/
    
        function __define () {
    	    "use strict";
    	
    	    // laeuft in module.evaluate();
    	    // oder inside einer factory();

            var id = null,
                dependencies = [],
                args = arguments,
                args0 = arguments[0],
                args1 = arguments[1],
                staticRequires, 
                originalLen, 
                factory = null,
                argc = args.length,
                exports, 
                module = this,
                child,
                modules;

            if (!args0) {
                return;
            }
            
            if (argc === 1) {
            
                if (isObject(args0)) {
                    exports = args0;
                } else if (isFunction(args0)) {
                    factory = args0;
                }
            }
            
            if (argc === 2 && Array.isArray(args0)) {
                dependencies = args0;
                factory = args1;
            }
            
            if (argc === 2 && isString(args0)) {
                id = args0;
                if (isObject(args1)) {
                    exports = args1;
                } else if (isFunction(args1)) {
                    factory = args1;
                }
            }
            
            if (argc === 3) {
                id = args0;
                dependencies = args1;
                factory = args[2];
            }
            
	    if (!id || module.toUrl(id) === module.toUrl()) {	// defines NO own namespace (anonymous module defaults to the namespace its found in)
		child = module; 
	    } else {
		child = new Module(id, module);
	    }      

	    if (cache[child.toUrl()] && cache[child.toUrl()].executed) {
		// console.log("happened already");
		return;
	    }

            child.dependencies = dependencies;
            
            if (typeof factory === "function") {

		child.type = "define";
        	child.factory = factory;
        	child.loaded = true;
        	// child.executed = false;
            
                if (child.executed) {
                    // console.log("factory: already executed");
                    return child.exports;
                    
                } else {
                    staticRequires = child.findStaticRequires(factory.toString());
                    if (staticRequires.length > 0) {
                        originalLen = dependencies.length;
                        staticRequires = staticRequires.filter(function (id) {
                    	    return dependencies.indexOf(id) === -1;
                        });
                        dependencies = dependencies.concat(staticRequires);
                    }
        	    child.dependencies = dependencies;
                    
                    if (dependencies.length > 0) {
                        child.require(dependencies, function () {
                            var args = arguments;
                            var modules = staticRequires.length > 0 ? 
                            [].slice.call(args, 0, originalLen) : 
                            [].slice.call(args, 0, args.length);
			    child.args = modules;
                            child.args = [].concat([child.require, child.exports, child.module]).concat(modules);
                    	    child.declare(factory, modules);
                    	    if (child.areMyChildrenComplete() && child.parent) {
                    		if (!child.parent.executed) child.parent.runIfMyModulesAreComplete();
                    	    }
                        });
                    } else {
                	// direkt keine dependencies zu laden
                        child.args = [child.require, child.exports, child.module, child.define];
                        child.declare(factory, child.args);
                        if (child.areMyChildrenComplete() && child.parent) {
                    	    if (!child.parent.executed) child.parent.runIfMyModulesAreComplete();
                    	}
                    }
                }
            }
            return child.exports;
        }
        
        
        function Module (id, parent) {
    	    /* Code in Power Constructor */
        }	

	var globalContextModule = new Module(null); // null erzeugt in Module ein Top-Level-Modul, ideal fuer ein globales require 

	Object.defineProperties(global, 
	    require: {
		value: function () [
		    "use strict";
		    globalContextModule.require.apply(null, arguments);
		},
		writeable: false,
		configurable: false
	    },
	    define: {
		value: function () {
		    "use strict";
		    __define.apply(null, arguments);
		},
		writeable: false,
		configurable: false
	    }
	});

    }(this));


    

Hier sind Sachen moeglich, die sonst nur mit vielen #define Macros und Casts zu machen sind...Und das schoenste ist, die Function ist ein gemeinsames Objekt und keine tausende verschiedene Signaturen, wie man in C Funktionen oder in Java wortwoertlich benennen. Objektorientierung, gute struktur(orient)ierte Progammierung, Funktionen, machen JavaScript hier zu einer tollen Sprache, die es wenigstens nicht ganz so genau wissen will, wie die Funktion heisst.

Zur Konstruktorfunktion Module () geht es hier.

Vererbung

Prototypisch

Es kann direkt von irgendeinem alten Objekt geerbt werden. Es kann aber auch der Prototype für ein Objekt definiert werden. Zum Schluss kann man den Konstruktoren und Protypen Kram in ein paar Methoden zusammenfassen und stark vereinfachen.

    var altesObj = { a: "abc", b: "def", c: 12 };
    function create(altesObject) {
	    function F() {}
	    F.prototype = altesObject;
	    return new F();
    }
    var neuObj = create(altesObj);    

    // neuObj instanceof F === true
    
    // Die Properties a, b, c sind nun unter neuObj.__proto__ zu finden
    // Aber mit neuObj.a, neuObj.b oder neuObj["c"] zu lesen und zu schreiben
    

Dafür gibt es heute die Object.create Methode.

Ich glaube, die kam von Crockfords Object.beget, zumindest hat er eine Menge getan, um ES3.1 bzw. ES5 zu dem zu machen, was es ist.

    // Crock hat die wohl erfunden. Das war wohl mal seine Object.beget.
    if (typeof Object.create !== "function"))
	Object.create = function (object, properties) {
	    function F() {}
	    F.protoype = object;
	    var result = new F();
	    if (typeof properties !== "undefined") {
		Object.defineProperties(result, properties);
	    }
	    return result;
	}
    

Damit lassen sich Objekte leicht erzeugen.

Es gibt einen Strawman zu Array.create

    var neuesObj = Object.create(altesObj, { "name": { value: "Edward", writeable: true }});	

    // neuesObj instanceof Object === true
    // neuesObj.name === "Edward"
    // Die Properties von olbObj sind dabei eine Ebene in der Prototype Chain von neuesObj gerutscht.
    

Der 2. Parameter von Object.create ist eine PropertyDescriptorMap, siehe Object Meta-API und Object.defineProperties.

	var neuesObj2 = Object.create(altesObj, {
	    "prop1" : {
		value: 12,
		writeable: false,
		enumerable: false,
		configurable: false
	    },
	    "prop2": {
		get: function () {
		    return Math.random();
		},
		enumerable: false
	    }
	});
    

Eine Funktion hat immer ein Prototype Objekt, dem man ein altes Objekt zuweisen kann, man kann aber, und manchmal ist das notwendig, die Properties einzeln an den prototype zuweisen.

    function O() {
    
    }
    O.prototype = new lib.EventTarget(); // 1. hier kann ich nicht prototype = { ... } setzen, weil ich woanders als vom Object erbe.
    O.prototype.constructor = O;
    O.prototype.method = function() {
	
    };
    new O().method();
    
    // 2. Ab es6 ist __proto__ ebenfalls Standard:
    //
    // O.protoype = { __proto__: new lib.EventTarget(),
    //	constructor: O, method: function () { } };

Was nicht geht ist:


    function O() {
    
    }
    O.prototype = lib.EventTarget.prototype; // ACHTUNG : Das erzeugt KEINE Kopie, sondern eine Referenz
    O.prototype.method = function() {
	
    };
    new O().method();
    
    console.log( O.prototype.method === lib.EventTarget.prototype.method ); // true
    
    // Es erzeugt eine Referenz. Statt einer Kopie.
    // Darum geht das nicht. Man schiesst sich damit den EventTarget prototype.
    

Warum ich einen Konstruktor angebe? Wenn ich das nicht mache, heisst der Protype "Object", wenn ich das mache, heisst er "O".

Pseudoklassisch

Mit einer Konstruktorfunktion sieht es mehr nach Class basierter Programmiersprache aus.

In ES6 wird es class geben, was in diese Form zurueckverwandelt. Richtige Klassen jedoch wird es nicht geben. Das baut weiterhin auf der prototypischen Vererbung auf.

Konstruktoren müssen mit dem Schluesselwort new instanziiert werden. "this" wird dann zur Referenz auf dieses neue Objekt.

In ES3 und ES5 standards mode wird, wenn man new weglässt anstelle des Objekts "this" u.a. zum Window Objekt, oder halt, als würde die Funktion normal aufgerufen werden. Da kann this dann das Objekt sein, zu dem die Funktion gehoert, oder halt das Window Objekt, wenn die Funktion niemandem gehoert, also nur lose im Code steht.

    
    function Constructor() {
	if (!(this instanceof Constructor)) {
	    return new Constructor();	// falls jemand "new" vergisst
	}
	this.name = "Edward";
    }
    Constructor.prototype = {
	constructor: Constructor, 
	set: function(n, v) {
	    this[n] = v;
	},
	get: function(n) {
	    return this[n];
	}
    };
    
    var object = new Constructor();
    object.set("nachname", "Gerhold");

    console.log( object.get("name") + " " + object.get("nachname") );

"this" bezieht sich auf das neue Objekt. Bekannt gemacht wird das dem Interpreter durch die Benutzung des Keywords "new" vor dem Constructor Aufruf.

Das this von normalen Funktionen, die keinem Objekt gehören, ist das Window Objekt. Im ES5 strict mode ist this lieber undefined als Window. Weil man sich ohne strict mode durch das Ausführen einer Konstruktor Funktion ohne "new" das global Objekt (window) 'ruinieren' kann.

Constructor Chaining

Wie man eine saubere Vererbungskette in JavaScript aufbaut.

Die Prototypkette ist linear, ein Objekt kann immer von einem erben, was wieder von einem geerbt haben kann und so weiter.

Anstelle sich Objekte zu Mixen, kann man sie sauber nacheinander voneinander extenden. Nur nicht gleichzeitig.

    // F
    function F() {
	this.name = "F";
    }

    // F -> F1
    function F1 () {
	this.one = 1;
    }
    F1.prototype = new F();

    // F -> F1 -> F2
    function F2() {
	this.two = 2;    
    }
    F2.prototype = new F1();
    
    // F -> F1 -> F2 -> F3
    function F3() {
	this.three = 3;
    }
    F3.prototype = new F2();
    
    console.dir( new F3() );
    
    // > Object
    // three
    //	proto->
    // 		two
    //   		proto->
    //	  			one
    // 	   				proto-> 
    //       					name

Und so weiter. So baut man sich eine saubere Vererbungskete auf.

Das Bild drueckt das Beispiel in umgekehrter Reihenfolge aus, F1 erbt von F2 und F von F1:

prototype chain

Das Auffinden von Variablen über den Prototype ist in den Engines optimiert und damit sehr fast und kein Bedenken wert, dass die Kette zu lang ist.

    
// Mir fiel die reduceRight Version ein.
// Ich kopier sie noch zum Multiple Inheritance Pattern    

function A() { this.a = 1; }
function B() { this.b = 2; }
function C() { this.c = 3; }
function D() { this.d = 4; }
    
var obj = [ A, B, C, D ].reduceRight(function (a, B) {
    if (typeof a === "function") {
	a = new a();
    }
    B.prototype = a;
    return new B();
});

console.dir(obj);    
console.log(obj instanceof A); // true
console.log(obj instanceof B); // true
console.log(obj instanceof C); // true
console.log(obj instanceof D); // true

    
// Diese Funktion erzeugt eine saubere Mehrfachvererbung.

function multipleInherit () {
    var arrayOfConstructors = [].slice.call(arguments);
    return arrayOfConstructors.reduceRight(function (b, A) {
	if (typeof b === "function") {
	    b = new b();
	}
	if (typeof A === "function") {
	    A.prototype = b;
	    return new A();
	} else throw new TypeError("wrong arguments: constructors expected");
    });
}

console.dir( multipleInherit(
    function A() { this.a = 1; },
    function B() { this.b = 2; },
    function C() { this.c = 3; },
    function D() { this.d = 4; }
) );


Jetzt mal mit versuchtem Mixin des Prototypes. b ist immer eine frische Instanz, darum mixe ich A.prototype dann in b und schreibe dann A.prototype = b.

function multipleInherit() {
    return  [].slice.call(arguments).reduceRight(function (b, A) {
	if (typeof b === "function") { // fuer den ersten Aufruf (zwei Konstruktoren).
	    b = new b();
	}
	if (typeof A === "function") {
	    if (typeof A.prototype === "object" && A.prototype !== null) {
		Object.keys(A.prototype).forEach(function (p) {		  
		    b[p] = A.prototype[p];					
		});
	    } 
	    A.prototype = b;
	    return new A(); // ab dem zweiten Aufruf ist b dann ein Objekt.
	} else {
	    throw new TypeError("wrong arguments: constructor functions expected");
	}
    });
}

function A() {
    this.a = 1;
}
A.prototype = {
    test: "already set a prototype",
    so: "this should become a mixin with the object of B with C with D"
};

var obj = multipleInherit(
    A,
    function B() { this.b = 2; },
    function C() { this.c = 3; },
    function D() { this.d = 4; }
);

console.dir(obj);
console.dir(obj.__proto__);
console.dir(obj.__proto__.__proto__);
console.dir(obj.__proto__.__proto__.__proto__);

{ a: 1 }
{ b: 2,
  test: 'already set a prototype',
  so: 'this should become a mixin with the object of B with C with D' }
{ c: 3 }
{ d: 4 }

Mixin

Die generelle Formel sollte neuesObjekt = mix(einObjekt, anderesObjekt, nochEinObjekt); sein. Es sollte aber auch moeglich sein, in ein vorhandenes Objekt zu mixen. Wie in der folgenden Funktion. neuesMixedObjekt = mix(altesMixedObject, anderesObjekt, nochEinObjekt, einObjekt); // wo altesMixedObject === neuesMixedObjekt nach der Funktion gilt. Den generellen Case mit dem frischen neuen Objekt, der mir gerade hierzu einfiel, den moechte ich hiermit aber voranstellen, eine gute mixin Funktion returnt ein neues Objekt mit einem mix aus allen uebergebenen Objekten.

	function mix() {
	    var mixin = {};
	    var args = [].slice.call(arguments);
	    if (args.length) args.forEach(function(o) {
		if (typeof o === "object" && o != null) {
		    Object.keys(o).forEach(function(p) {
			// mixin[p] = o[p];
			try { // wegen writeable:false, configurable:false, beim ueberschreiben
			    Object.defineProperty(mixin, p, Object.getOwnPropertyDescriptor(o, p));
			} catch (ex) {
			    console.log("Error assigning property: "+p);
			    console.dir(ex);
			}
		    });
		}
	    });
	    return mixin;
	}
	
	var emptyObj = mix();
	var mixed = mix({ a: 1 }, { b: 2 }, { c: 3 });
	console.dir(mixed);
	// { a: 1, b: 2, c: 3 }

    

Die Funktion mixt mixin in obj und ueberschreibt properties, falls overwrite true ist.

	function mix(obj, mixin, overwrite) {
	    // mutiert obj mit mixin
	    if (typeof mixin === "object" && typeof obj === "object" && mixin !== null && obj !== null) 
		Object.keys(mixin).forEach(function (p) {
		    if (typeof obj[p] === "undefined" || overwrite) {
	    		obj[p] = mixin[p]; // augment obj mit mixin
		    }	    	     
	        });
	    else 
		throw new TypeError("obj, mixin: wrong arguments. Objects expected");
	    return obj;
	}
    

Man kann einem Objekt einfach Operationen und Variablen von mehreren Objekten zuweisen und entweder mit bind binden, mit einer function, die es mit call und apply bindet, wrappen, man kann auch nur kopieren und sich auf die Integrität verlassen, man kann auch den Konstruktor rufen, das Mixin beziehungsweise die Augmentation, oder das hinzufügen von Properties bei einem Objekt ist in JavaScript kein Problem.

Augmentation

Hinzufügen von Methoden durch einfaches Zuweisen von neuen Methoden.

Das ist in etwa das, was auch bei einem Dekorator geschieht.

Gemeint ist in diesem Fall aber das einfache Zuweisen von Properties.

Auf die Art und Weise kann man in JavaScript ein Objekt extenden.

In streng typisierten Sprachen ist das gar nicht so einfach, weil das alles erstmal in Klassen deklariert sein muss. Dafuer gibt es dann abstrakte Interfaces, aber das ist nicht Thema dieses Dokuments.

	// aus addy osmanis largescalejavascript, was einen mediator nutzt
	installTo: function (obj) {
	    obj.subscribe = subscribe;
	    obj.publish = publish;
	}
    

In C++ ist das zum Beispiel unmöglich, Objekte einfach so zu augmentieren. Geschweige denn zu mixen. In JavaScript ist hingegen kein Problem. In C++ schon.

    // Pseudo C++ Code
    class C {
        char a = "a";
        C() {}
        ~C() {}
    } c;
    
    // Das hier gibt es gar nicht....
    int main(int argc, char **argv) {
	c.func = void func () { }
    }
    // Das kann man unmoeglich mit g++ kompilieren
    // Ich wuesste schon was mit Funktionszeiger und 
    // dann kann man die Adresse wechseln, oder abstract,
    // aber das wars dann auch schon 
    

Der Dekorator zum Beispiel dekoriert oder schmueckt mit neuen Attributen und Operationen...

Also hier wäre Platz für das Dekorator Pattern.. Gut.

Traits

In PHP kann man seit einiger Zeit traits wiederverwenden. Das sind Codeschnipsel, die in die Klasse eingefuegt werden.

    <?php
	trait XY {
	    function x () {
	    }
	    function y () {
	    }
	}
    ?>
    
    // Hab PHP fast vergessen, hehe
    

Traits kann man in JavaScript auch selbst kombinieren.

Man nimmt ein Objektliteral als Trait

	var trait = {
	    x: function () {}
	    y: function () {}
	};
    

Ein ausgeklügeltes Beispiel einer Traits Library gibt es in einem Google Tech Talk. In Changes to EcmaScript Harmony Highlights. Dort werden Propertydescriptormaps gelesen, und die Traits haben Funktionen wie resolve, um Konflikte aufzuloesen, sowie compose und create um einen Trait zu erzeugen, und um das Objekt mit dem Trait zu verbessern.

Nichtsdestotrotz, das Objektliteral den Trait weist man dann dem Objekt zu.

	var target = {};
	
	function assignTrait (target, trait) {
	    Object.keys(trait).forEach(function (key) { target[key] = trait[key]; });
	}
    
	assignTrait(target, trait);
    

So einfach lassen sich Objekte als Traits verwenden. Und wie gesagt, im Talk von Tom van Cutsem (ist unten verlinkt), Harmony Highlight, Proxies und Traits gibt es die Variante mit PropertyDescriptorMaps.

    // Ich denke mal, das ist die kürzeste Fassung
    
	Object.keys(trait).forEach(function (key) {
	    var map = Object.getOwnPropertyDescriptor(trait, key);
	    Object.defineProperty(target, key, map);
	});
    
    

Doch die Traits Library kann mehr als nur den Trait zuweisen. Und dürfte minified und concatted nur ein paar (wenige) Bytes haben. Siehe code.google.com/p/es-lab/ oder Tom van Cutsem (vrije Uni Bruessel, auch TC-39 Mitglied).

Multiple Inheritance durch Prototype Chaining

Oder wie man das richtig nennen soll. Jedenfalls kann man dafür einen Algorithmus definieren.

// Mir fiel die reduceRight Version ein.
// Ich kopier sie noch zum Multiple Inheritance Pattern    
    
function A() { this.a = 1; }
function B() { this.b = 2; }
function C() { this.c = 3; }
function D() { this.d = 4; }    

var obj = [ A, B, C, D ].reduceRight(function (b, A) {
    if (typeof b === "function") {
	b = new b();
    }
    A.prototype = b;
    return new A();
});

console.dir(obj);    
console.log(obj instanceof A); // true
console.log(obj instanceof B); // true
console.log(obj instanceof C); // true
console.log(obj instanceof D); // true

Diese Funktion muss noch etwas optimiert werden. Wenn A bereits einen Prototype hat, muss man b entweder mixen, oder den alten prototype. Eine einfache Zuweisung loescht den.

// Aber schonmal als library funktion ausgearbeitet:

function multipleInherit() {
    return  [].slice.call(arguments).reduceRight(function (b, A) {
	if (typeof b === "function") { // fuer den ersten Aufruf (zwei Konstruktoren).
	    b = new b();
	}
	if (typeof A === "function") {
	    if (typeof A.prototype === "object" && A.prototype !== null) {
		Object.keys(A.prototype).forEach(function (p) {
		    b[p] = A.prototype[p];
		});
	    } 
	    A.prototype = b;
	    return new A(); // ab dem zweiten Aufruf ist b dann ein Objekt.
	} else {
	    throw new TypeError("wrong arguments: constructor functions expected");
	}
    });
}

var o;
console.dir( o = multipleInherit(
    function A() { this.a = 1; },
    function B() { this.b = 2; },
    function C() { this.c = 3; },
    function D() { this.d = 4; }
) );

console.log(o instanceof A); // true
console.log(o instanceof B); // true
console.log(o instanceof C); // true
console.log(o instanceof D); // true

Pseudo-Multiple Inheritance durch Mixins

Die "dreckige" Vererbung, wo instanceof false wird, aber duck-type tests true ergeben und problemlos funktionieren, weil alle Properties und Methoden aber vorhanden sind.

In JavaScript gibt es keine Mehrfachvererbung (class A extends B, C, D) wie in anderen Sprachen. Die Prototypkette ist linear und geht von prototype zu prototype zu prototype. Was mit reduceRight oder reduce sogar zu vereinfachen ist, jedenfalls fiel mir was ein, steht im letzten Kapitel.

Selbstgemacht

Ich mische einfach alle Klassen und baue mir einen Superkonstruktor, der alle Konstruktoren auf meinem Objekt ruft, um es zu initialisieren.

Nachteile - Closure Variablen sind nicht mehr dabei, wenn eine Funktion, die eine Closure nutzt, aus ihrer Umgebung gerissen wird. Kommt aber normal ja auch nicht vor, dass man sowas schreibt.

Hier erstelle ich einen neuen Konstruktor, der alle Konstruktoren ruft und auf ein und das gleiche Objekt anwendet.

    // Constructor = lib.Object.extend( Constructor, SuperConstructor, {}, {} );
    // Constructor = lib.Object.extend( Constructor, [ SuperConstructor, AnotherConstructor ], {}, {} );

	var Objekt = {	
    	    extend: function (Sub, Gen, proto, stat) {
			var l, p, P, i;
			if (Array.isArray(Gen)) { // Multiple Inheritance: Die Funktion kopiert die prototypes
				i = 0, l = Gen.length;
				for (; i < l; i++) {
					if (P = Gen[i].prototype) {
						for (p in P) {
							// hier kein hasOwnProperty Filter, bitte!
							Sub.prototype[p] = P[p];
						}
					}
				}
				var MultiConstructor = function () {	// Erstellt einen Parentconstructor, der alle ruft
						var i = 0;
						for (; i < l; i++) {
							Gen[i].apply(this, arguments);
						}
				};
				Sub.prototype.constructor = MultiConstructor;
			} else {
				if (Gen.prototype) {			// Single Inheritance: oder kopiert nur einen prototype
					for (p in Gen.prototype) {
						    // hier
						    Sub.prototype[p] = Gen.prototype[p];
					}
					var Constructor = function () {	// und ruft nur einen Constructor 
							Gen.apply(this, arguments);
					};
					Sub.prototype.constructor = Constructor;
				}
			}
			if (proto) {	// fuege dem Prototype von Sub alles von proto hinzu
				for (p in proto) {
					if (Object.hasOwnProperty.call(proto, p)) {
						Sub.prototype[p] = proto[p];
					}
				}
			}
			if (stat) {	// fuege der Funktion Sub statische Properties zu
				for (p in stat) {
					if (Object.hasOwnProperty.call(stat, p)) {
						Sub[p] = stat[p];
					}
				}
			}
			return Sub;
		}
	};
	function Construct () {
	    this.constructor.apply(this, arguments);	// <-- this.constructor(); 
								// wird bei meiner Funktion benoetigt (und zwar Sub.prototype.constructor) 
								// um alle Konstruktorregeln der Oberklassen auf meine Unterklasse anzuwenden
	    this.firstname = "Edward";				
	}
	Construct.prototype.constructFunc = function () {
	    console.log("constructFunc");
	};
	function SuperConstruct () {
	    this.lastname = "Gerhold";
	}
	AnotherConstruct.prototype.superFunc = function () {
	    console.log("superFunc");
	};
	function AnotherConstruct () {
	    this.nickname = "Eddie";
	}
	AnotherConstruct.prototype.anotherFunc = function () {
	    console.log("anotherFunc");
	};
	
	/* Construct = */ Objekt.extend(Construct, [ SuperConstruct, AnotherConstruct ] );
	var construct = new Construct();
	construct.firstname === "Edward";
	construct.lastname === "Gerhold";
	construct.nickname === "Eddie";
	construct.constructFunc();
	construct.superFunc();
	construct.anotherFunc();
	dir(construct);
	// Construct
	// firstname: "Edward"
	// lastname: "Gerhold"
	// nickname: "Eddie"
	// __proto__: Object
	// anotherFunc: function () {
	// constructFunc: function () {
	// constructor: function () {	// Erstellt einen Parentconstructor, der alle ruft
	// superFunc: function () {
	// __proto__: Object

Da instanceof hier nicht mehr greift, weil die Namen der gemixten Objekte nicht in der Prototype-Chain auftauchen, empfehle ich duck-typing um zu prüfen. Man guckt dann, ob das Objekt gewünschte Funktionen oder Eigenschaften enthält, die versichern können, dass es sich um das richtige Objekt handelt.

Das super_ Pattern

Nochmal, die Urform der prototypischen Vererbung.

    // Douglas Crockford unterrichtet Object.create in seiner Urform.

    var create = (function () {
	function F () {};
	return function (p) {
	    F.prototype = p;
	    return new F();
	};
    }());

Die hier arbeitet mit Constructor und Prototyp. Die erzeugten Objekte heissen dann nicht F sondern wie der Constructor.

    var create = function (F, p) { 
	    F.prototype = p; // Modifiziert F (alle properties sind veraenderlich trotz call by value sonst)
	    F.prototype.constructor = F;
	    return new F();
    };

Dann gibt es noch verschiedene Extend Methoden. Die hier nimmt zwei Konstruktoren, und weist den Prototypen von F ein neues Objekt von G zu. Ausserdem erzeugt man eine superclass oder super_ Property, wo der prototype noch einmal gespeichert wird.

    // Das ist ein Remake von mir. Ich frage mich, warum man die superclass
    // ueberhaupt speichert, wo die Prototype Kette linear ist.
    // anderseits, __proto__ wird jetzt erst standardisiert, ich weiss warum,
    // weil sie nicht richtig nachgucken konnten, ob die ueberschriebene 
    // Property exisierte. 

    var extend = function (F, G) {
	function H () { this.constructor(); };
	H.prototype = G.prototype;
	G.prototype.constructor = G;
	F.prototype = new H();
	F.prototype.constructor = F;
	F.superclass = G.prototype;
	return F;
    };
    
    function F () { this.first="Edward"; }
    F.prototype = Object.create(null); // Hier gab es mal einen Bug mit = null, was ein anderen Objekt ist als das.
    F.prototype.method = function () {
	if (typeof this.superclass.method === "function") {
	    console.log("Ich habe die Methode gefunden");
	    console.log("Ich rufe sie und sie spricht nur einmal");
	    this.superclass.method.call(this.superclass);
	}
    };
    function G () { this.last = "Gerhold"; }
    G.prototype = Object.create(null);
    G.prototype.test = function () {
	console.log("Du hast call benutzt. Du kannst mich sehen.");
    };
    G.prototype.method = function () {
	console.log("Hallo, du hast mich gefunden");
	if (typeof this.test === "function") {
	    this.test();
	}
    };
    var H = extend(F, G);
    var h = new H();
    console.dir(h);

Man hat aber auch andere Moeglichkeiten "super" Methoden zu konservieren.

    F.prototype.test = function () {
	console.log("Ich mache was ganz anderes");
    };
    F.prototype.super_test = function () {
	G.prototype.test.apply(this, arguments);
    };

__proto__

Achtung: __proto__ ist Standard ab Ecma Script next (6)!!! Alle Informationen hier sind in der kommenden Version also Standard. Das Feature wird haeufiger eingesetzt und verwendet.

Mehr Information: http://wiki.ecmascript.org/doku.php?id=strawman:magic_proto_property

__proto__ ist der geheime Link, der in einem Objekt auf das prototype Objekt zeigt. __proto__ gehoert in Version 3 und 5 nicht zum nicht zum ECMA Standard. Aber das Feature ist so beliebt, Mozilla hat es, v8 hat und Opera glaube ich auch

    var o = {
	"name" : "object"
    };
    var p = {
	method1: function () {}
	__proto__: o
    };
    console.log(p.name);
    // object    
    

__proto__ hat den Vorteil, dass man ein F.prototype = { __proto__: o, ... }; zuweisen kann, anstatt F.prototype = new O(); F.prototype.method1 = function () {}; F.prototype.method2 = function () {};. Weiter oben habe ich das schonmal gesagt, dass man nicht F.prototype = {} und F.prototype = new O() gleichzeitig schreiben konnte.

    // GEHT NICHT
    var o = {
	"name": "object"
    };

    function F () {
    }
    F.prototype = o;	
    F.prototype = {		// klar, das ueberschreibt o.
	method1: function () {
	}
    };
    

In Version 5 ist es noch nicht Teils des Standard, __proto__ zu implementieren.

    // GEHT
    var o = {
	"name": "object"
    };
    function F () {
    }
    F.prototype = {
	__proto__: o,
	method1: function () {
	}
    };

Mit proto kann man Objekte erben lassen, die keine Konstruktor Funktion haben (ideal fuer Singletons, die auf Objektliteralen basieren)

    var singleton = {
	__proto__: { 
	    "d":"d", 
	    "e":"e", 
	    "f":"f" 
	},
	"a": "a",
	"b": "b",
	"c": function () {
	    console.log(this.d);
	}
    };

    // test
    singleton.c();
    // "d"

    var singleton2 = {
	__proto__: new function Constructor() { // Das ist ein anderes Objekt von dem Einzelstueck 2 erbt.
	    this.a = "a";
	}
    };
    
    // test
    console.log(singleton2.a);
    // "a"

Interessant ist halt, dass man so ein Singleton Objekt ohne Constructor-Prototype erben laasen kann.

The text "__proto__" can break a webapp

Interessanter Beitrag von Axel Rauschmayer, den ich gerade entdeckt habe. Wenn man bei Google Docs __proto__ zu Beginn des Dokuments eingibt, haengt sich Google Docs auf. Wenn man einen @__proto__ Tweet verfasst, und draufklickt, dann hat die Profile Summary nur eine Title Bar.

http://2ality.com/2012/11/proto-breaks-webapps.html

Verwandt oder nicht verwandt: Die "constructor" Property in Objekten. Wenn man, das habe ich bei Crockford erfahren, z.B. einen Wordcount schreibt, und ein Wort davon "constructor" heisst, gibt es auch einen Error.

	// Das Wordcount Object sollte nicht von Object.prototype sondern mit __proto__: null starten.
	
	var wc = Object.create(null); 
	
	// Object.create(null);
	// Damit kann man den "constructor" key als normalen Key verwenden.
	
	var word;
	// Annehmen, 
	text.split(/\s/).forEach(function (word) {
	    // word is gerade "constructor";
	    wc[word] = typeof wc[word] === "number" ? wc[word]+1 : 1;
	});
    

new Operator vergessen?

Funktionen, die mit einem Großbuchstaben beginnen, sind Konstruktorfunktionen.

Diese sind immer mit new zu benutzen. In ECMAScript 3 (dem alten JavaScript ´99-´09) ist bei einem normalen Funktionsaufruf this gleich dem globalen Objekt window.

    function Construct () {
	this.name = "Eddie";
    }
    
    var o = Construct(); // <--- new fehlt!!!
    
    o === undefined;
    window.name === "Eddie";

Damit kann man sich bislang nur das window Objekt kaputtmachen und das Programm zum Absturz bringen.

Der strict mode weiss das schonmal grundsaetzlich zu verhinden, weil bei einem Funktionsaufruf this erstmal undefined ist. Das Programm sollte nun eine Fehlermeldung ausgeben.

Richtig ist es aber, einfach den new Operator richtig zu verwenden. Immer dann, wenn man mit einem Konstruktor ein Objekt erzeugen will.

    function Construct () {
	this.name = "Eddie";
    }
    
    var o = new Construct();
    
    o instanceof Construct; // true
    o.name === "Eddie";		// true

    // o
    // > Construct
    // > name: "Eddie"
    // > __proto__: Construct

instanceof

Instanceof sagt, ob bei element instanceof Element in der Prototypekette von element das Element vorkommt.

Es ist mit manchmal nützlich, das zu prüfen, aber gerade bei Mixins kommt öfter mal ein falscher Wert raus, weil der gesuchte Mixin-Typ nicht gefunden werden kann. Da hilft dann Duck-Typing (walks like a duck, quacks like a duck, is a duck - das Objekt hat ein length Attribute, eine push Methode, eine Pop Methode, wahrscheinlich ist es ein Array...

Instanceof lässt sich vielfältig einsetzen.

    function Construct (options) {
	// if (this === window || this === undefined) {  
	if (this instanceof Construct === false) {
	    return new Construct(options); 		// (*) wird bei o = Construct() gerufen
	}
	this.name = "Eddie";
	// arbeite mit options
    }

    var o = Construct();	// (*)

    o instanceof Construct === true;
    o.name === "Eddie";
    

Perfektioniert geht das so. Beim oberen Beispiel gab es Probleme, sobald ich arguments brauchte.

    function Construct () {
    	var construct = this instanceof Construct ? this : new Construct(),
    	args = Array.prototype.slice.call(arguments, 0, arguments.length);
    	if (args.length > 0) {
    		args.forEach(function(key, i) {
    			construct[i] = key;
    		});	
    	}
    	construct.name = "Eddie";
    	return construct;
    }

    var o = Construct("a","b","c");    // ohne new
    o[0] === "a";
    o[1] === "b";
    o[2] === "c";
    
    var p = new Construct("d","e","f");    // mit new
    p[0] === "d";
    p[1] === "e";
    p[2] === "f";
    

Nebenbei koennt ihr lesen, wie man aus arguments einen richtigen gleichgrossen Array macht.

Am leserlichsten ist dennoch

   		var o = new Construct();
   

Object.getPrototypeOf

Zu instanceof gibt es noch am Object Constructor eine function Object.getPrototypeOf(obj), womit man den direkten Prototyp ermitteln kann.

	var prototyp = {
	    __proto__: null,
	    a: 1,
	    b: 2,
	    c: 3
	};
	
	var obj = Object.create(prototyp, {
	    "d": {
		value: 4
	    }
	});
	
	var p = Object.getPrototypeOf(obj);
	console.log( p === prototyp );
	// true
   
   

(Call by) Reference, Call by Value

Objekte und Arrays sind Referenzen. new gibt unter anderem ein frisches Objekt. Jede Funktion bringt eine neue Closure. Die umliegende Closure wird sich von mehreren Aufrufen geteilt, die innere wird neu erzeugt.

Eine Objektreferenz ist nicht gleichzusetzen mit Call by Reference (Funktionsaufruf mit &Referenzuebergabe).

	/*
	<div id="node"></div>

	var node = document.getElementById("node");
	*/
	var node = document.createElement("div");
	node.id = "node";
	var othernode = node;
		// Achtung, das ist nur eine Referenz und kein neues Objekt.
	
	othernode.id = "reference";
	console.log( node.id === "reference" );
	// true

Im DOM kann man uebrigens mit othernode = node.cloneNode(true); eine neue Node erzeugen. Für Objekte nimmt man Object.create(object); oder man erzeugt ein neues Objekt mit new Constructor();

	var othernode = node.cloneNode(true);
	// true bedeutet, dass die Kinder mitgeklont werden.
	
	var object = { name: "object" }
	var otherobject = Object.create(object);
	
	// Entspricht in etwa der create Funktion
	

Man kann mit einer Funktion ein Objekt mutieren.

	function stuff(o, n, v) {
	    o[n] = v;
	}
	var o = {};
	stuff(o, "aetsch", "baetsch");
	o.aetsch === "baetsch"; // true
	

Funktionsargumente sind bloss Values (Call by Value)

	function tausche(a, b) {
	    var t = a;
	    a = b; b = c;
	}
	
	var x = 10; y = 20;
	tausche(x,y);
	x === 10
	y === 20
	

Crockford hat mal gesagt, das swappen nicht moeglich waere. Das ist richtig. Er sagte auch, dass, wenn man ein Objekt als argument uebergibt und es in der Funktion null setzt x = 12; var funky = function (v) { v = null }; funky(x); x === 12 ? console.log("true") : console.log("false"); // true, es trotzdem nicht null ist, weil die Funktion nur was über den Wert weiss.

Fakt ist, sage ich noch dazu, dass man die Innereien eines Objekts in der Funktion aendern kann und die Innereien des Objekts dann veraendert sind (siehe oben). Aber shallow (flach) hat man keine Chance, an das Objekt ranzukommen.

Call by Value
- node
> var o = { a: "a" }, p = { b: "b" }, swap = function (a, b) { var t = a; a = b; b = t; };
undefined
> o
{ a: 'a' }
> p
{ b: 'b' }
> swap(o,p)
undefined
> o
{ a: 'a' }
> p
{ b: 'b' }

Ob man die Parameter tauschen kann, wenn man anstelle der Buchstaben arguments benutzt? Nein, das geht auch nicht.

    // arguments ist auch nicht anders.
    function swap (a, b) {
	var t = arguments[0];
	arguments[0] = b;
	a = arguments[1];
    };
    
    n = new Number(11);
    m = new Number(12);
    swap(n,m);
    console.log(n);
    console.log(m);

    // nichts..n ist 11 und m ist 12

Was aber geht, ist destructuring assignment, damit wird das swappen von values ganz einfach gemacht, ohne temporaere dritte Variable. In den neuen JavaScript Versionen wird folgendes gehen:

var x = 11, y = 12;
[y,x] = [x,y]; // swappt die Values (ca. y=x.valueOf() und x=y.valueOf() gleichzeitig)
x
// 12
y
// 11

Singleton

Wie eben erwähnt lässt sich das Singleton, von dem es nur eine Instanz gibt, quasi mit {} einem Objektliteral definieren.

	var singleton = {
	    data: "daten"
	};
    

Man kann das Singleton Pattern aber auch, wie in anderen Programmiersprachen schreiben..

    
	function makeSingleton (Konstruktor) {
	    "use strict";
	    
	    if (typeof Konstruktor !== "function") 
		throw new TypeError("constructor function for the instance expected");
	    var instance = null;
	    
	    return Object.freeze({
		getInstance: function () {
		    return instance ? instance : (instance = new Konstruktor());
		}
	    });
	};    
	
	
	// Ein Beispielkonstruktor fuer den makeSingleton
	function Obj() {
	    if (!this instanceof Obj) return new Obj();
	    this.name = "Eddie";
	    this.date = new Date();
	}
	Obj.prototype = {
	    toString: function () { return "[Object Obj]"; }
	};
    
	// testen.
	var objSingleton = makeSingleton(Obj);
	var obj = objSingleton.getInstance();
	
	// Das Singleton Objekt ist manipulationssicher

	objSingleton.getInstance = null; // wird nicht gehen (frozen)
    
    

Wenn man sich den ganzen Overhead aber anguckt, sollte man in JavaScript lieber keine überzogenen Dinge planen und einfach Objektliterale {} nehmen.

Variablen

Noch werden Lokale Variablen mit var deklariert. Merkt euch den Begriff "function scope" f&uum;r die Sichtbarkeit.

In der nächsten Edition werden let und const hinzukommen und var kann durch richtige organisierte let ersetzt werden.

	    var variable;			// undefined
	    var variable2 = [];			// Array
	    var variable3 = {};			// Objekt
	    var variable4 = 4;			// Number
	    var variable5 = false;		// Boolean
	    var variable6 = /regex/;		// Regex
	    var variable7 = "Eddie";		// String
	    var variable8 = function () {};     // functions koennen anonym zugewiesen werden.
	    var variable9 = new Constructable;  // () koennen bei new, nur bei new, weggelassen werden.
	

Variablen werden in der Funktion nach oben gehoben, ganz egal, wo man sie definiert. Der Vorgang nennt sich Hoisting.

	// bla
	// bla
	var f = function () {
	};
	// bla
	var name = "eddie";
	function g () {
	}
	
	

wird zu

	var g = function g() { // function g() wird KOMPLETT nach oben gehoben
	}
	var f = undefined;	// var f = function wird erst mit UNDEFINED initialisiert
	var name = undefined;
	// ------ hier ist die unsichtbare grenze zwischen speicher und code ----
	// hier ist die erste zeile code nach allen gehoisteten variablen und funktionen:
	f = function () {	
	};
	// bla
	name = "eddie";
	

Hoisting: Damit man die Namen nutzen kann, bevor ihr Deklarationsteil erscheint. Dazu muss der Interpreter erst den Source 1* lesen, hoisten, und dann weiter optimieren und dann letztendlich das Skript starten (alles in 0,0001ms)

// node hoist.js bringt die Wahrheit nochmal
console.dir(F);		// function F
console.dir(G);		// undefined
console.dir(H); // throwt einen ReferenceError weil H not defined ist (nicht undefined, sondern gar nicht vorkommend)
function F() {
}
var G = function () {}; // wird als var G = undefined nach oben gehoben und ist ab hier nutzbar,
			// weil hier G dann den Wert function bekommt.

EcmaScript arbeitet noch im Function Scope. Das heisst definiert irgendwo in der Funktion ist sichtbar überall in der Funktion.

Hier sehen wir nochmal, wie eine Funktion lokale Variablen erzeugen kann, und man auf die Variablen der äusseren Funktion (Ausnahme this, aber dafür kann man eine Referenzvariable wie that nehmen) zugreifen kann, beziehungsweise, wie sie ausserhalb ihrer Funktion nicht sichtbar sind.

	    var name = "Eddie";
	    (function () { // Closures realisieren das Information Hiding 
		var name;
		console.log(name); // hier ist name undefined (siehe 
		name = "Edward";		
		console.log(name); // hier ist name Edward
	    }());
	    console.log(name); // Hier ist name der von oben.
	    
	    // undefined
	    // Edward
	    // Eddie
	

In ECMAScript 5 sind Namen fuer Objekt-Properties erlaubt, die in ECMAScript 3 zu einem Fehler fuehren. So entstand z.B. Element.className anstelle von Element.class, weil class ein reserviertes Wort war.

Damals wurden doch die Javabegriffe reserviert. Ausserdem sieht der Parser scheisse aus, weil er die Objektschluessel nicht von Schluesselworten auf Anweisungsebene unterscheiden kann.

RESERVIERTE WORTE in ES3 sind NICHT MEHR RESERVIERT in ES5.

Was aber APIs wie das DOM (className, htmlFor) nicht verändern wird.


    // Failt in ES3 class, static, return, private sind reservierte Worte
    // Schluesselposition, solange keine Quotes drum sind.
    
    // In JSON werden immer Quotes rumgetan, damit gehen die Keys auch in ES3.
    
    var o = {
	class: "abcde",
	static: "sdfs",
	return: "sdfsdf",
	private: "sdfsdf"	
    };
    
    // ebenso wie:
    // Punktposition
        o.class = "irgendwas"; 
    
    // Darum erfand man bei Netscape bereits "className" (class=) oder "htmlFor" (label for=)

    // --> Top: Wurde in Ecma Script 5 geaendert.
    
	

Auf der globalen Ebene eines Codes, ausserhalb des Objekts oder innerhalb von Funktionen, ist class aber weiterhin ein reserviertes Schlüsselwort. Also weder in Schlüssel- noch in Punktposition. Für ES6 gibt es dafür übrigens einen Vorschlag, wie man mit einer Syntax zu class JavaScript Objekte erzeugen kann.

Ach, dann nochwas zu className, was mir beim probieren auffiel: Im attribute Knoten einen Elements ist der attribute[i].nodeName Wert natürlich === "class" und !== "className". Ist ja wohl logish, aber dass ihr das schonmal wisst. Das gilt auch für element.htmlFor beim "for" eines <label>s. Da ist der attributes[x].nodeName = "for".

    // --> Das hier ist die Loesung fuer reservierte Worte, der String-Key
    
    // Subscript Notation object[name] anstelle der Punktnotation
    o["class"] = "sdfjsldfj";
    o["return"] = "sdfsfjd";
    
    // Die Worte als "string" in der Schluesselpostion
    var o = {
	"class": "sdjfk",
	"return": "jfsdf"
    };
    // letzteres ist gleich dem JSON subset,
    // was sich vom ECMAScript Standard nur durch zwei Leerzeichen PS und LS 
    // unterscheidet
    // Der auch reservierte Worte durch die String Keys nutzen kann.

null

Wertigkeit: false, in anderen Programmiersprachen gibt es einen Nullpointer, der genutzt wird, um Werte zu löschen, aber die Variable beizubehalten.

var object = null; if (object) { try { console.log(JSON.stringify(object)); } catch (e) { console.log(e.message); } } else { console.log("object is deleted or null"); }

Besonders tricky ist, dass der typeof null === "object" ist. Dass ein typeof nicht unbeding ausreicht, wenn man jeden Wert erhalten könnte, zum Beispiel, wenn jemand eure API benutzt.

	function x(ob) {
	    if (typeof ob === "object") {
		console.log(ob.a);
	    }
	}
	
	x({ a: 1 });
	x(null);	// Fehler
	
    // Ok
	
	function x(ob) {
	    if (typeof ob === "object" && ob !== null) {
		console.log(ob.a);
	    }
	}
	
	x({ a: 1 });
	x(null);	// klaro
    

In ES6 wird der typeof operator null === "null" wahrscheinlich repariert sein. Ausprobieren kann man es in V8 z.B. mit dem --harmony_typeof flag

	node --harmony_typeof -e 'console.log( typeof null === "null" )'
	// true
    

Irrtum? - Oktober 2012 - Im EcmaScript Wiki kann man typeof null wieder durchgestrichen sehen, also durchgestrichen. Weil der Operator zuviele Programme bricht. Ich denke mir, wenn man ES6 einführt und die Hälfte eh bricht, ist das doch kein Problem, diese wichtige Korrektur zu machen. Ich finde den doppelten Vergleich if (typeof obj === "object" && obj !== null) ebenso ätzend, wie ich auch mit leben könnte. Aber halt nur könnte. Wenn ES6 doch eh bricht. Dann brecht richtig. .

undefined

undefined ist der Wert, mit dem vars initialisiert werden.

    var name;
    console.log(name);
    // undefined
    

undefined ist der Wert, der nach einem delete übrigbleibt

    var name = "Edward";
    delete name;
    console.log(name);
    // undefined
    

So kann man prüfen, ob eine Variable vorhanden ist, ohne dass das System abstuerzt, was mit if (sjsdfhsdf) passieren würde, wenn es sjsdfhsdf nicht gibt:

    
    if (typeof sdhsdfhsfd === "undefined") {
	console.log("sdhfsjfh ist nicht definiert worden");
    }
    
    if (typeof global !== "undefined" && global.toString() === "[object global]") {
	console.log("Könnte nodejs sein. Waere toString nicht definiert, Absturz.");
    } else {
	console.log("Obwohl es global nicht gibt, stuerzt es nicht ab.");
    }
    

Man kann undefined anstelle von null durchgängig benutzen, muss aber damit vertraut sein, dass null von anderen als null-Pointer eingesetzt wird und irgendwas = null gesetzt wird. irgendwas = undefined ginge auch. Aber es ist mehr der ideale default Wert für uninitialisierte Parameter und so .

	function y (name) {
	    console.log(name);
	}
	
	y("Edward");
	// Edward
	y();
	// undefined
    

Bei Objekten kann man sich darauf verlassen, dass, wen das Objekt existiert und man nicht typeof obj.name === "undefined", sondern obj.name === undefined nimmt, das Programm nicht abstürzt. Alle nicht definierten Variablen geben hier ebenso die Variable undefined zurueck.

	var obj = {};
	
	console.log(obj.name === undefined); // true
	
	// beachtet: es wurde nicht typeof und der String undefined
	// sondern die Variabel undefined und ein strict equal === verwendet.
    

Type Coercion

    null == undefined   // true
    null === undefined  // false
    

Hoisting

Variablen werden vom JavaScript Übersetzer automatisch nach oben gehoben.

Variablen werden nach oben gehoben und mit undefined initialisiert. An der Stelle wo die Originalinitialisierung stand, wird dann der Wert zugewiesen.

    // Aus
    function x() {
	var s = 19;
	function y() {
	}
	if (true) {
	    var t = 20;
	} else {
	    var z = 20;
	}
    }
    
    // Wird automatisch
    function x() {
	var s = undefined, t = undefined, z = undefined, y = function y () {};
	s = 19;
	if (true) {
	    t = 20;
	} else {
	    z = 20;
	}
    }
    
    // Das heisst 

Daraus ergibt sich der Function Scope

Function Scope

Variablen einer Funktion sind überall in der Funktion sichtbar, das heisst, in Kontrollstrukturen und inneren Funktionen.

"A variable defined anywhere in a function, is visible everywhere in a function." -- Douglas Crockford. (Act III, Function The Ultimate)
    function outer(a, b) {
	var c = "sichtbar";
	function inner() {
	    if (c === "sichtbar") {
		console.log(a);
		console.log(b);
		console.log(c);
	    }
	}
	inner();
    }
    outer(1,2);
    // 1
    // 2
    // sichtbar

Block Scope, d.h. nur innerhalb von { } Klammern sichtbar, wie in C++, Java, PHP und den meisten anderen Sprachen, wird zusammen mit anderen Neuerungen im nächsten JavaScript ES6 durch das neue Wort let anstatt var ermöglicht.

Probleme mit Function Scope:

In C läuft dieses Beispiel, in JavaScript nicht, weil sich die "innere" i Variable immer wieder auf 5 zurueck gesetzt wird, weil sie nur einmal existiert und in der gesamten Funktion gescoped ist.

    // Block Scope in C:
    
    for (int i=0;i<10;i++) {
	// eine int i
        for (int i=0;i<5;i++) {
    	    // eine andere int i
	}
    }

Achtung: Dass man zweimal var i schreibt, wird noch nichtmal durch eine Meldung gemeldet. Selbst, wenn ich "use strict"; eingebe, erhalte ich in dem Fall keine Meldung.

    // Function Scope in JavaScript
	
    // Achtung: Dieser Code haengt den Browser auf.
    // Wenn ihr node nehmt, duerft ihr das Fenster zumachen.
    
    for (var i=0; i<10; i++) {
	// var i
        for (var i=0; i<10; i++) {
    	    // die gleiche i, welche aussen auch zaehlt
	    // i ist am Schluss 5
	    // dadurch laeuft die auessere Schleife unendlich
	    // weil sie immer auf 5 zurueckgesetzt wird
	    // Schon beim ersten Durchlauf springt sie von 0 auf 5
	    // Und dann bleibt sie haengen
	    // Weil eine Variable im Function Scope nur einmal existiert
	}
    }
    
    // Function Scope II
    // Es heisst Function Scope, weil der auf die Funktion beschraenkt ist
    // das begreift alle fors und while und ifs mit ein, aber keine function
    
    function a () {
	var i = 12;

	function b () {
	    var i = 13;
	    console.log(i);
	}
	function c () {
	    var i = 14;
	    console.log(i);
	}
	console.log(i);
	b();
	console.log(i);
	c();
	console.log(i);
    }
    
    a();
    // 12 (a)
    // 13 (b)
    // 12 (a)
    // 14 (c)
    // 12 (a)

Damit lässt sich nochmal leicht begreifen, dass die Sichtbarkeit auf die Funktion beschraenkt ist. Was man aber noch kann, ist von inneren Funktionen auf die aeusseren zugreifen -aber nicht umgekehrt-.

    // <- Hier ist alle Global Scope

    // Jetzt geht es in den lexikalischen Scope einer funktion
    function scope () {
	// Bin die aeusserste Huelle vor dem Globalen Scope
	var accessible = "Hello World";
	function inner() {
	    // Greife auf den Scope von scope zu
	    console.log(accessible);
	}
	inner();
    }
    scope();
    // Hello World

Ein Workaround fuer "this", welches nicht gleich ueber alle Funktionen geht, ist var that von Douglas Crockford. Ein Abstraktion des Bezeichners für das Objekt in der Funktion ermoeglicht noch weitere Muster.

    ({
	fun: function () {
	    var that = this;
	    function inner() {
		console.log("Hello");
		document.removeEventListener("click", inner);
		that.func();		
	    }
	    // Real World
	    document.addEventListener("click", inner, false);
	    return that;
	}
    }).fun().toString();
    // [object Object]

Durch () um das Objekt erhalte ich das Objekt als Ergebnis von () und kann darauf .fun() rufen. Darum ist das Objekt in ({ fun:function(){} }).fun() gehuellt. Und weil die Funktion fun that zurueckgibt, habe ich nochmal .toString() gerufen, um das zu nutzen. (Just for fun, weniger sinnvoll.)

this

Die Ausnahme ist "this", welches sich immer auf das Objekt bezieht, dem die Funktion gehört. Der this Objekt kann mit .call und .apply beeinflusst werden und oder mit bind gebunden werden.

"this" kann man sich mit var that = this; oder var self = this; in die inneren Funktionen holen, die möglicherweise, wie hier in der Listener Funktion (this === document), in einem anderen Context (mit einem anderen this, aus einem anderen Objekt) aufgerufen werden.

    function O() {
	var that = this;
	this.a = "a";
	document.addEventListener("click", function (e) {
	    // this === document  (this instanceof O === false)
	    console.log(that.a); 
	}, false);
    }
    var o = new O(); 

var that = this

Eine Referenz ueber Funktionsgrenzen transportieren.

Weil man in den inneren Funktionen kein this hat, was der aeusseren Funktion gehoert, sondern ein eigenes (fuer ES6 wird das diskutiert, arrow functions zu nutzen, deren this fix das der aeusseren Funktion ist), muss man, das Function Scope Prinzip nutzen, und eine Variable erzeugen, die in den inneren Funktionen auf das gewuenschte Objekt was hinter dem "this" stand, anzusprechen.

    
	var o = new (function Construct () {
	    var thatConstruct = this;	// eine Referenz auf this
	    
	    function insideConstruct() {
		thatConstruct.inside = true; // Die Referenz wird benutzt
	    }
	    insideConstruct();
	    console.log( this.inside ); // und hat funktioniert
	
	    return thatConstruct;    
	})();
    
    

Man kann mit call oder apply oder bind natürlich auch ein this binden. Aber das funktioniert nicht fuer jeden Anwendungsfall. Für den hier schon.

    	var o = new (function Construct () {
	    function insideConstruct() {
		this.inside = true;
	    }
	    insideConstruct.call(this);
	    console.log( this.inside );
	})();
    

Es gibt halt immer wieder Punkte, wo man gerne die Variable haette, die gerade noch this gewesen ist.

In diesem Fall it die Variable xhr das gleiche, wie that.

	var xhr = new XMLHttpRequest();
	xhr.open("GET", "/", true);
	xhr.onload = function (e) {
	    
	    // in der Eventfunktion 
	    // ist this === xhr 
	    // und auch === e.target des Event Objekts e

	    console.log(xhr.responseText);
	    
	};
	xhr.send(null);
    

Nochmal, man braucht diese Referenz um in inneren Funktionen, die in Funktionen geschrieben sind, das richtige Objekt zu referenzieren.

    
    function SomeThing() {
	var that = this;
	that.item = function () {
	    return that[i];
	};
	that.assign = function (y) {
	    var that = this;
	    function helper(i) {
		// Diese Funktion braucht diese Variable "that" 
		// um SomeThing zu referenzieren.
		that[i] = i * y;
	    };
	    for (var i = 0; i<10; i++) {
		helper(i);
	    }
	};
	return that;
    }
    SomeThing.prototype = new Array(10);
    
    var st = new SomeThing();
    st.assign(44);
    console.dir(st);
    
    

Information Hiding

Information Hiding und private Properties werden durch Closures gesetzt.

	function hideLikeParnas () {
	    "use strict";
	    var private1;
	    var private2;
	    var private3;
	    return Object.freeze({
		xs1: function () { return private1; },
		xs2: function () { return private2; },
		xs3: function () { return private3; }
	    });
	}
    

call und apply

Dynamisches bestimmen von this zur Laufzeit. Mit Function.prototype.call(object, arg1, arg2[, ...]) und Function.prototype.apply(obj, array)

Damit ist es Moeglich, eine Funktion eines anderen Objekts oder eine lose Funktion so aufzurufen, als ob sie zu dem Objekt gehoert, an das sie dynamisch gebunden wurde.

Die andere Moeglichkeit ist es, dadurch zu verhindern, dass eine Funktion mit "window" (browser) oder "self" (worker) "global" (node.js), aufgerufen wird und this.* Zuweisungen dann Schaden anrichten können. Besonders dort, wo man davon ausgehen kann, dass nicht so geübte Programmierer die Interfaces benutzen. Beispielsweise die breite Masse, da JavaScript heute ein Stück Allgemeinbildung ist.

	function O() {
	}
	var o = new O();
    
	function set(n, v) {
	    this[n] = v;
	}
	
	set.call(o, "a", "a");
	console.log( o.a === "a" ); // true
    
	function set2() {
	    set.apply(o, arguments);
	}
	set2("b","b");
	console.log( o.b === "b" ); // true

    

.call und .apply nehmen als ersten Parameter das Objekt, das in dem Fall das this der Funktion die man callt oder applied wird und darauf folgend die Parameter. .call nimmt einzelne Paramter, .apply nimmt neben dem Objekt einen Array [] mit den Argumenten (*).

(*) Hier kann man beispielsweise das reservierte Wort arguments übergeben, in dem alle Argumente der Funktion, in der wir gerade sind speichert.

Function.prototype.call

myfnctn.call(contextObject, Einzelne, Parameter, alle, nacheinander, ab, dem, zweiten);

Call bindet das Contextobjekt, das "this" zur Laufzeit entsprechend dem ersten Parameter des Aufrufs.

Alle Parameter, die der Funktion call übergeben werden, werden dann ab dem zweiten Parameter einzeln übergeben.


	function callDemo = function (a,b,c) {
	    console.log(Object.toString.call(this));
	    console.log(a);
	    console.log(b);
	    console.log(c);
	};
    
    
	callDemo("abc", "bcd", "cde");
	// [object Window]
	// "abc"
	// "bcd"
	// "cde"
	
	
	// Richten wir mal was ein.
	var context = { 
	    toString: function () { return "[object Eddie]"; } 
	};
	
	
	callDemo.call(context, "abc", "bcd", "cde");
	// [object Eddie]
	// "abc"
	// "bcd"
	// "cde"
	
	
	var wrapper = function (a,b,c) {
	    callDemo.call(context, a, b, c);
	};
    

Wer die Parameter nicht einzeln übergeben kann, weil er den Array oder arguments nicht einzeln bestimmen kann, sollte apply ausprobieren.

Function.prototype.apply

myfnctn.apply(contextObject, [Array,of,arguments]);

Apply bindet das Contextobjekt, das "this", zur Laufzeit entsprechend dem ersten Parameter des Aufrufs.

Die Funktionsargumente werden als 2. Parameter in Form eines Arrays, oder eines Arguments Objekts, oder eines Array-like Objekts [hat .length und Ziffernindex], uebergeben.

	
	function applyDemo = function (a,b,c) {
	    console.log(Object.toString.call(this));
	    console.log(a);
	    console.log(b);
	    console.log(c);
	};
	
	var context = { 
	    toString: function () { return "[object Eddie]"; } 
	};
	
	applyDemo.apply(context, [1,2,3] );
	// [object Eddie]
	// 1
	// 2
	// 3
    

apply ist eine unverzichtbare Funktion, um Parameter zu übergeben.

Ich denke mir, call({}, 1,2,3) wäre noch ersetzbar mit apply({}, [1,2,3]), aber umgekehrt keines Falls. Man kann die Anzahl der Parameter nicht immer hardcoden.

	// Gerade für die Variable arguments unentbehrlich
	
	var wrapper = function () {
	    applyDemo.apply(this, arguments);
	};
	wrapper.apply(context, [1,2,3]);
	// [object Eddie]
	// 1
	// 2
	// 3
	
    

bind

var fn = fnctn.bind(objct, fnc);

Mit Function.prototype.bind(object) kann man eine Funktion fast an ein Objekt binden. Die Funktion wird dann mit dem "this" des Objekts ausgeführt.

Die Funktion gibt eine neue Funktion zurueck, die man dann immer wieder rufen kann, weil sie fest gebunden ist, die Originalfunktion wird nicht veraendert, die kann man dann neu binden, beziehungsweise mehrere von erstellen.

Anders als bei call und apply ist die Bindung nicht einmalig, sondern dauerhaft. Dadurch aendert sich die Originalfunktion aber nicht, da bei einem bind eine neue Funktion zurueckgegeben wird.

	// Anwendungsfall: Eine Funktion, die this verwendet.
	// Zum Beispiel eine normale Objektmethode
	// Jetzt wollen wir sie _mehrmals_ zu einem bestimmten Objekt rufen.
	// Als ob sie von dem Objekt her waere.
	// Darum binden wir sie.
	
	function x(a,b,c) {
	    this.a = a;
	    this.b = b;
	    this.c = c;
	}
	
	var myObject = {};
	var myObject.x = x.bind(myObject);
	
	myObject.x(10,11,12);
	console.log(myObject.a); // 10
	console.log(myObject.b); // 11
	console.log(myObject.c); // 12
    

Das wird noch sehr nützlich, weil man die Funktionen und Objekte dynamisch verwenden kann, mal dafuer, mal mit dem, und so weiter.

Die bind Funktion hat ihren Ursprung in den Ajax-Libraries, ich glaube, aus der legendären prototype.js Library.

Man kann sich die Funktion auch selbst schreiben:

	// selbstschriebene Bind Funktion
	function ownFnBind(object, func) {
	    return function() {
		return func.apply(object, arguments);
	    }
	}
	var boundFn = ownFnBind(myObject, x);
    

Oder halt manipulationssicher in ES5, dass man die Funktion nicht veraendern kann.

	// selbstschriebene Bind Funktion mit Object.freeze
	
	function ownFnBind(object, func) {
	    return Object.freeze(function() {
		return func.apply(object, arguments);
	    });
	}
	var boundFn = ownFnBind(myObject, x);
    

Die Funktion gibt eine Funktion zurueck, die die uebergebene Funktion func mit dem Contextobjekt object aufruft.

In ES5 ist die Bind Funktion etwas anders definiert.

	if (!Function.prototype.hasOwnProperty("bind")) {
		Function.prototype.bind = function (object) {
		    var func = this, slice = [].slice
		    args = slice.call(arguments, 1);
		    return function () {
			return func.apply(object, args.concat(slice.call(arguments)));
		    };
		};
	}
    

Oder halt mit Object.freeze, dass die Funktion nicht mehr configurable ist.

	if (typeof Function.prototype.bind !=== "function") {
	    Function.prototype.bind = function (object) {
		"use strict";
	        var func = this, args = [].slice.call(arguments, 1);
	        return Object.freeze(function () {
		    return func.apply(object, args.concat([].slice.call(arguments)));
		});
	    };
	}
    
    

Das war nur eine Idee, eine Funktion bereitzustellen, aehnlich .call, und .apply, die antelle des NEW keywords gerufen werden kann...

	
	    // Edwards .constructable() zu .call, apply, .bind
	    Function.prototype.constructable() = function construct () {
		return new this.constructor();
	    };
	    
	    // Eine Object.construct faellt mir dabei direkt auch ein.
	    Object.prototype.reonstruct = function reconstruct () {
		// if (this.constructor) 
		return new this.constructor();
		// else throw new Error("no constructor");
	    };
	    
	    // Usage:
	    function f () { this.name = "Edward"; }
	    f.constructable();
	    // { name: "Edward" }
	    f.constructable().reconstruct();
	    // { name: "Edward" }
	
	

Self executing functions

IIFE (immediately invoked function expression)

Ich nenne sie Iffies oder Iffy (Spitzname).

function hi() { return "hallo"; }(); kann man nicht ausfuehren. Aber wenn man runde Klammern um die Funktion legt, wird aus dem function Statement eine function Expression, die direkt ausgefuehrt werden kann.

    (function () { return "hallo"; })();

Noch anschaulicher ist es, wenn man die runden Klammern für die Invocation nach innen holt.

    (function () { return "hallo"; }());

Es gibt noch mehr Varianten, wie man den Compiler in den entsprechenden Zustand bewegt.


    !function() {
	console.log("Hallo!");
    }();

    // Hallo
    // true
    
    
    // !func gibt true zurueck, weil die Funktion undefined zurueckgab
    // und das mit !-not invertiert wird.

Oder so


    var x = function() { /* krewl */ }(); 
    
    x === undefined; // logisch

Das ganze ist eh eine Abkuerzung fuer das hier.

    // original
    function main() {
    
    }
    main();
    
    // abkuerzung
    (function main() { }());

Anynonyme Funktionen: Werden anders als benannte Funktionen nicht im Variablen-Kontext auftauchen. Wenn man eine anonyme Funktion einer Variablen zuweist, ist die Variable belegt, der function.name aber "".

Vorteil von Iffies

anonyme Funktionen

Anonyme Funktionen erzeugen keine Variablen im Contextobjekt und werden normal als dynamische "Wegwerfunktionen" benutzt.

	
	setTimeout(function () {
	    // Ops
	}, 123);

    

Oder halt, weil man zu faul für den Namen ist, als Kurzzschreibweise, taucht im Debugger aber als (anonymous function) auf

	window.addEventListener("click", function (e) {
	    
	}, false);
	
    

Es werden anonyme Funktion auch als grosse Closure benutzt, wie man von tausenden Programmen kennt.

	(function (window, document) {
	    
	    // Programm Code
	
	}).apply({}, window, window.document);
    

Was bei anonymen Funktionen nicht drin ist, ist, dass man sie spaeter identifizieren kann Beziehungsweise schreibt der Debugger dann (anonymous function) zusammen mit dem Call Stack. Ich habe eigentlich keine Probleme damit.

Ich glaube als "Lambda" oder "First Class Value" ist die function () {} einfach unbezahlbar.

	
	window.addEventListener("click", function (e) {
	    
	}, false);

    

Eine unbenannte Funktion kann man, wenn man sie an eine Variable zuweist, mit einer benannten Funktion vergleichen, das Verhalten ist identisch, man kann sie referenzieren, dennoch ist die .name Property an der Funktion nicht gesetzt.

	var assigned = function (e) {
	
	};
	// Der Name wird dadurch nicht gesetzt
	console.log(assigned.name); 
	// ""

	window.addEventListener("click", assigned, false);
	// Aber man kann referenzieren
	window.removeEventListener("click", assigned);
    

benannte Funktionen

Benannte Funktionen haben eine .name Property, die kein "" Empty String ist.

	function fuu () {
	
	}
	console.log(fuu.name);
	// "fuu"
	
    

Erzeugt man eine benannte Funktion, bei der Zuweisung an eine Variable, wird der name der benannten Funktion nicht dazu genutzt, noch eine Variable anzulegen, es bleibt bei der, an die die neue Funktion zugewiesen wurde.

	
	var fuu2 = function baa () {
	    // baa wird nicht als Variable angelegt.
	};
	
	console.log(fuu2.name);
	// "baa"
	
	// gucken, ob auch baa angelegt wurde?
	console.log(typeof baa === "function");
	// falsch (undefined)
	// throwt einen ReferenceError
	
	
    

Wird im globalen Context window.foo, oder in einem lokalen Context (einer Funktion, einer Objektmethode) nur zu foo und taucht auch nicht im Context-Objekt als Property auf. var nimmt nur das Window Objekt sichtbar auf.

    
	function F () {
	    function argz() {
	    }
	}
	var f = new F()
	
	console.log( typeof f.argz === "undefined" );
	// true
	
	console.log( F.name );
	// F
    

Benannte Funktionen gelten als einfacher zu debuggen, weil man Namen auf dem Stacktrace hat.

Dann gibt es noch etwas zu benannten Funktionen: Sie bieten eine Referenz auf sich selbst an.

	var x = function test (y) {
	    if (typeof y !== "number") y = 0;
	    if (y < 100) test(y+1); // man hat eine Referenz auf die Funktion
	};
	
	
	// innerhalb der Funktion, ist der naechste Name, der drauf passt
	// der der benannten Funktion, darum kann diese auch test heissen.
	
	
	var y = function test (z) {
	    if (typeof z !== "number") y = 100;
	    if (z > 0) test(y-1); // man hat hier eine neue Referenz 
	};
	

	function test () { // wird nach oben gehoben und ist ueberall lesbar
	    console.log("ich bin eine andere Funktion");
	}
	
	// die drei Namen test kommen sich durch die zwei Variabeln nicht in die Quere
	
    

Da die Funktion test direkt einer Variable zugewiesen wurde, wird sie nicht im globalen (oder funktionslokalen) Namensraum angelegt.

Closure

Lexikalischer Schluss

sowie Function Scope statt Block Scope

Closure bedeutet nichts anderes, als dass die aussere Funktion die inneren Variablen (den Zustand des Automaten, also der Funktion) am Ende einschliesst. Wunderbar daran ist, dass nach Beendigung der Funktion der Kontext erhalten bleibt, dass heisst, die Variablen für jegliche Ergebnisse, auf die zum Beispiel der Rueckgabewert der Funktion, eine Funktion oder ein Objekt, zugreift, immernoch zur Verfügung stehen. Und das Beste ist, jede neue Instanz bekommt seine eigene Kopie dieser Variablen mit. So kam man irgendwann auf das Module Pattern, was heute wohl jeder kennt.

    function freshClosure () {
	var closedOver = "me";
	var encapsulated = "me too";
	function f () {
	    return closedOver;
	}
	f.g = function () { // functions koennen Variablen "aussen" (am eig. Function Object halt) mit . Speichern.
	    return encapsulated;
	};
    }
    
    var f = freshClosure();
    console.log(f());	// me
    console.log(f.g()); // me too();
    
    

Der andere Vorteil der Closureklammer ist, dass sich mehrere Instanzen die Variablen in der Closure teilen koennen, und auf die gleiche Variable zugreifen koennen.

    
	var Creator = (function () { // 1. Diese Funktion wird sofort ausgefuehrt.
	
	    var counter = 0;	// 2. Diese Variable wird eingeschlossen

	    function Creator () { / 3. Die Funktion erzeugt Objekte
		if (!this instanceof Creator) return new Creator();
		this.id = counter;	// 4. Die Objekte nutzen den Counter
	    }
	    Creator.prototype.toString = function () {
		return "gizmo "+this.id;
	    };
	    return Creator; // Die funktion Creator wird jetzt sofort an var Creator zugewiesen.
	}());
	
	var gizmo1 = Creator();  // hier greift (!this instanceof Creator) [ES5: use strict]
	var gizmo2 = new Creator();
	
	console.log(""+gizmo1); // gizmo 1
	console.log(""+gizmo2); // gizmo 2
	
    
    // Crockford maessiges Module Pattern, bei ihm begreift man Closure schnell

    // Crockford hat das Module Pattern weiter verbessert. Seht seine new_constructor
    // function in seinem ES5 Talk und Act III Function The Ultimate. 
    
    // Dieses Module Pattern gibt Crockford bereits in seiner 2007er Klasse preis.
    
    var module = (function () {
	var private;				// erfuellt auch information hiding
	function privateFunction() {
	    console.log(private);
	}
	return {
	    publicProperty: "Hello World",
	    publicFunction: function() {
		console.log("public function");
	    },
	    protectedFunction: function() {
		private = "blah";
		privateFunction();
	    }
	    // , makePublic: privateFunction
	};
    }());
    
    // module.private === undefined
    // module.privateFunction === undefined
    // --- wenn das Objekt existiert geben nicht existente Properties einfach undefined ohne Absturz zurueck

    console.log(module.publicProperty);
    module.publicFunction();
    module.protectedFunction(); // kein Fehler, da der Context zu private erhalten blieb

    // Hello World
    // public function
    // blah

Das Beispiel führt sich selbst aus, und gibt ein Objekt zurück. Achtung! Das Selbstausführen der Funktion hat nichts mit dem Closure Prinzip zu tun. Kann man aber drüber lesen, dass darüber von uns Anfängern spekuliert wurde, ob es das hat, und es wurde wiederholt drauf hingewiesen, dass nicht.

Am Ende jeder Funktion vollzieht sich ein Einschluss alles innerem.

    
	function f() {
	    var encapsulated = "closure";
	    
	    function stillAccess () {
		console.log(encapsulated);
	    }
	    function stillWriteAccess () {
		encapsulated = Math.random();
	    }
	    return {
		getProp: stillAccess,
		setProp: stillWriteAccess,
		unsetProp: function () {
		    encapsulated = "closure";
		}
	    };
	}

	// Die Variable encapsulated wurde durch die Closure eingeschlossen.
	
	var obj = f();
	obj.getProp();
	// closure
	obj.setProp();
	// 0.349250295092
	obj.getProp();
	// 0.349250295092	
	obj.unsetProp();
	obj.getProp();
	// closure
	
    

Den macht man sich zu nutze. Der Funktionsabschluss wurde extra aus der Lehre der Programmiersprachen übernommen. Man kann damit herrliche Sachen machen, weil man Zustaende erhalten kann, ohne die Variablen von Funktion zu Funktion neu uebertragen zu muessen, oder immer eine globale Struktur upzudaten. Mit Closure geht sowas in der Art automatisch. Der Zugriff auf die Variablen bleibht erhalten. In JavaScript von allen inneren Funktionen hinauf bis zu der, in der die var definiert war, in der sich die Funktionen die drauf zugreifen befinden.

Über dem Objekt sind in der Funktion Variablen definiert, die nun für das Objekt erhalten bleiben.

Zumm Modulepattern Beispiel. module erhät das Objekt, was mit return zurückgegeben wird. Durch das Closureprinzip sind die private Properties immernoch an das Objekt gebunden. Diese sind für andere unerreichbar, weil man die Funktionsklammer nicht knacken kann.

Ich zeig mal was anderes, wie man Variablen in einer Objekt Definition in einem Constructor erzeugt. Mit so ähnlichen Beispielen habe ich das auch schnell verstanden gehabt.


function Closure() {

    // closed overs
    var private = "private";
    var privateMethod = function(x,y) {
	return { x:x, y:y };
    };
    
    // statics 
    this.getThePoint = function(x,y) {
	return privateMethod(x,y);
    };
    this.getPrivate = function() {
	return private;
    };
}

var c = new Closure();
c.getThePoint(1,2);	// { x:1, y:2 }
c.getPrivate();		// "private"

Der Vorteil bei dieser Art ist, dass man Variablen benutzen kann, die man über mehrere Methoden weiterreichen kann, auf die man spaeter über die Objektvariablen immernoch Zugriff hat, sofern sie gerufen werden.

Was an dem Closure Pattern auch wichtig war, war das this.property = expression Assignment, was man noch durch Object.defineProperty schoener und verstaendlicher ausdruecken kann. Gerade diese Objektroutinen haben Zugriff auf die gesamte Closure des Konstruktors, und damit lassen sich "Power Constructor" Funktionen erzeugen.

    function ClosurePattern () {
    
	var encapsulate = [];
	var captured = {};
	var private = "";
    
	// this.propA = "Edward;
	Object.defineProperties(this, {
	    "propA": {
	        value: "Edward"
	    },
	    "propB": {
		get: function () {
		    // Zugriff in der Closure ueber die gesamte Lebenszeit des Objektes
		    // Bei all diesen Verschachtelungen hier im Code, muss man ClosurePattern als Huelle sehen
		    return private;
		}
	    },
	    "propC": {
		set: function (v) {
		    private = v;
		}
	    }
	    "funA": {
		value: function () {
		    encapsulated.forEach(function (a) {
			console.log(a);
		    });
		}
	    },
	    "funB": {
		value: function (a) {
		    encapsulated.push(a);
		}
	    }
	});	
    }
    // __proto__ ist in diesem Fall empty und hat No Properties (auch kein .hasOwnProperty o.ae. von Object.prototype)
    ClosurePattern.prototype = Object.create(null);

    var obj = new ClosurePattern();
    obj.funB("Eddie");
    obj.funA();
    // Eddie
    obj.propA;
    // ""
    obj.propB = "Setter";
    obj.propA;
    // Setter

Das mächtige an diesem Pattern ist der dauerhafte Zugriff auf die Variablen. Wenn niemand mehr das Objekt braucht und keine Referenzen mehr drauf zeigen, auf die gesamte Kapsel, in diesem Fall das erzeugt Objekt, dann wird auch die Closure verschwinden, und das Objekt vom Garbage Collector geholt. Aber solange das im Code vorkommt, ist alles da, was man will. (Das ist wie Vegas in so einer Klammer hat einer von Yahoo mal gesagt.)

    var sayClosedOver = (function() {
	
	var closedOver = "and still closed over!";
	var counter = 0;

	return function () { // Waere das ein Konstruktor, teilen sich alle Instanzen _einen_ Counter!!
	    counter += 1;
	    return counter + "* "+ closedOver;
	};
    
    }());
    
    sayClosedOver();
    // 1* and still closed over!

    sayClosedOver();
    // 2* and still closed over!

Was hier passiert ist, dass bei der Zuweisung der sich selbst ausfuehrende Teil "unsichtbar" wird, und die zurueckgegebene Funktion an die Variable geht. Durch das Closure Prinzip bleibt der restliche Inhalt aus der selbstausfuehrenden Funktion fur die zurueckgegebene Funktion erhalten und die zurueckgegeben Funktion kann da normal drauf zugreifen.

    // Kreieren wir einen anderen lexikalischen Schluss
        
    document.querySelector("button.start").onclick = (function () {

	    var flag = false;	// Diese bleibt erhalten dank der Schliessung
				// oder soll man "des Schlusses" sagen (?)

	    return function (e) {
		flag = !flag;
		if (flag) {
		    document.body.style.backgroundImage = "";
		    document.body.style.backgroundColor = "black";
		    document.body.style.color = "white";
		} else {
		    document.body.style.backgroundImage = "url(jsbg.jpg)";
		    document.body.style.backgroundColor = "white";		
		    document.body.style.color = "black";		
		}
	    };

    }());

Achtung, das geht nicht mehr: Da ich ein background-image verwende. Ich versuche mal eine Anpassung. Wenn das mit der image Anpassung wieder geht, und ob das dann geht. Moment.

Das hier ist eine Funktion, die eine Funktion annimmt, und eine zurueckgibt, die die uebergebene Funktion entweder einmalig ausfuehrt, oder das Ergebnis bei jedem weiteren Aufruf aus dem Cache (hier der Variable in der Closure) liefert.

var once = function( f ) {
    var cached;
    return function () {
	if (f) {
	    cached = f.apply(f, arguments);
	    f = null;
	}
	return cached;
    };
};

Revealing Module Pattern

Revealing heisst "enthüllen". Ich denke mal, damit ist das enthuellen der Closure Inhalte gemeint, die ans Objekt attached dann revealed werden.

Von der Rap Gruppe Above the Law gibt es übrigens einen Klassiker mit dem Titel "Time will Reveal" (Die Zeit wird es enthüllen).

Das ist eine Verbesserung des Module Patterns, was intern function statements und vars verwendet und sie dem Objekt, was sie zurueckgibt einfach zuweist. Es ist mir in Addy Osmanis Design Patterns Buch als Ergänzung zum Module Patterns erst aufgefallen, aber gesehen habe ich es in praktischem Code schon oft.

	var module = (function () {

	    function aFn () {   }
	    
	    function bFn () {  }
	    
	    function cFn () {
		bFn();	// rufe die Public b function von unten auf
	    }
	    return {
		a: aFn,
		b: bFn,
		c: cFn,
	    };
	    
	}());
	
	// Bedeutet: Das obere Beispiel ist nicht so umstaendlich wie das hier.
	// Hier kann man nicht einfach aFn() schreiben, wie beim Revealing Pattern
	    var module = (function () {
		var private = 77;
		var obj = {
		    aFn: function () { console.log(private); }
		};
		obj.bFn = function () {
		    obj.aFn();
		};
		return obj;
	    }());
    

Durch diese "losen" Definitionen muss man nicht erst obj. vor den Funktionsnamen schreiben, wenn man die Funktionen aufrufen will. In dem kurzen Skelett fällt das nicht auf, aber in der Praxis hat man das schon öfter mal dauernd, dass man innerhalb der Funktionen die anderen ruft, und dann muss man nicht immer das Objekt voranstellen. Addy Osmani sagt, von einem Christian Heilmann waere das Revealing Module Pattern, dem das zu aufwendig war, mit einem benannten Objekt zu arbeiten. Er sagt das nicht wortwoertlich so, am Besten ihr lest selbst. Da sind gute Patterns drin. Ich wollte die schon hier einbringen (immer wenn ich die Quelle nenne, ist da auch eine). Hehe.

Mir kommt gerade noch die Idee, wie man es tamper-proof macht, das geht dank der Closure Variablen, die nicht gefrozen sind sehr gut, weil das Module die Funktionalitaet erhaelt, nur nicht mehr veraendert werden kann.

Sicheres Revealing Module Pattern (ECMAScript 5)

	var module = (function () {
	    "use strict"; // lexical scope
	    function aFn () {   }
	    function bFn () {  }
	    function cFn () {
		bFn();	
	    }
	    return Object.freeze({ // frozen object
		a: aFn,
		b: bFn,
		c: cFn,
	    });
	}());
	
	module.x = 12;
	// geht nicht, da frozen
	module.a();
	module.b();
	module.c = function () {
	    console.log("niemals");
	};
	module.c();
	// wurde nicht ueberschrieben, ist immernoch cFn
    

Shared state (variablen) im Module Pattern

Nachteil des Module Patterns: Variablen werden unter allen Instanzen geteilt.

    
var Demo = (function () {

    var a_ = 0;
    var b_ = 10;
    
    function Demo (a,b) {
	this.set(a,b);
    }
    Demo.prototype.set = function (a, b) {
	a_ = a;
	b_ = b;
	return this;
    };
    Demo.prototype.get = function () {
	return [a_, b_];
    }
    return Demo;
    
}());

    var d1 = new Demo(10, 12);
    var d2 = new Demo(11, 13);
    
    d1.get();
    
    // 11, 13 statt 10, 12

Loesung Das in einer function immer neu machen, anstatt die Funktion das einmalig einkapseln zu lassen..


function Demo () {

//    return new (function () {    

	var a_ = 0;
	var b_ = 10;
    
	function Demo (a,b) {
	    this.set(a,b);
        }
        Demo.prototype.set = function (a, b) {
	    a_ = a;
	    b_ = b;
	    return this;
	};
	
	Demo.prototype.get = function () {
	    return [a_, b_];
	}
    
	return new Demo;
	
// }());

}

    var d1 = Demo(10, 12);
    var d2 = Demo(11, 13);
    
    d1.get();
    // [10, 12]
    d2.set(14,10);
    d1.get();
    // [10, 12]

    

Seht ihr, auf diesem Weg kann man private Variablen erzeugen, die in einer Closure stecken fur mehrere Instanzen immer wieder frisch erzeugen..

Man kann die IIFE sogar komplett entfernen, wie man sieht, ich habe sie zur Veranschaulichung kurz auskommentiert.

Closures und Loops

Wenn man Funktionen in einem Loop schreibt, die ueber eine Variable closen sollen, wird man am Ende den finalen Wert der Schleife erhalten.

var a = [];
    for (var i=0; i<10; i++) {
	a.push(function () {
	    console.log(i);
	});
    }
    a.forEach(function(f) {
	f();
    });

    // 10
    // 10
    // 10
    // 10
    // 10

Das ist ein bekanntes Problem, zum Beispiel, wenn man EventHandler definiert.

Das ist mir das erste mal passiert, als ich im Chrome per Extension Bookmarks iterieren wollte.

    var names = [ "a", "b", "c" ];
    for (var i=0; i<3; i++) {
	var name = names[i];
	el[i].onmouseover = function (e) {
	    overlay[i].innerHTML = name;
	};
    }
    
    // Es wird auf allen drei Elementen der 3. Overlay angezeigt.

Das ist halt so beim Function Scope, der Closure, und der Tatsache, dass bei einer Iteration nicht der aktuelle Wert der Iteration (der gesamte Block) gecaptured wird, sondern einfach der Loop durchlauft und die Funktion dann ueber dem Ergebnis closed.

Abhilfe schaffen kann man sich mit einer eigenen IIFE im Loop, um einen Scope zu erzeugen, die Variablen, die mit reinsollen, muss man als Parameter uebergeben, sonst hat man das gleiche Problem wie vorher mit dem scope.Close


    var names = [ "a", "b", "c" ];
    for (var i=0; i<3; i++) {
	(function (i) {
	    var name = names[i];
	    el[i].onmouseover = function (e) {
		overlay[i].innerHTML = name;
	    };
	}(i));
    }

Sowie mit den ES5 Array Methoden, die ebenfalls eine frische Closure erzeugen.

    names.forEach(function (name, i) {
	el[i].onmouseover = function (e) {
	    overlay[i].innerHTML = name;
	};
    });

Man sollte [aber nur nebenbei] ueberlegen, wie tief man seine Callbacks in Funktionen kapselt. Heute Computer leisten viel und die Kette schnell auf, aber bei rechenintensiven Sachen mit vielen Nutzern und Elementen kann das schnell ein Brecher werden, hier geht die Kette nur zwei Funktionen tief, aber es kann noch viel mehr werden. [Aber nur nebenbei. Übrigens. Vor ein paar Jahren hat man die Bytes im Speicher noch einzeln gezählt.]

Funktionale Patterns

Diese Funktionen erstellen in ihrer Funktion Strukturen, mit Closure und Co, privaten und public Methoden, und geben dann die Struktur zurück.

Anderes als mit der prototypischen Vererbung und all ihren Schwierigkeiten, ruft man hier einfach die Funktion, kriegt genau das, was man will und muss sich keine Sorgen um "new" machen, weil der Programmierer der Funktion all das in der Funktion geregelt hat.

Objektkonstruktoren, die keine richtigen Konstruktoren sind, können mit einem Kleinbuchstaben verwendet werden. Werden sie mit "new" verwendet, hat das normalerweise keinen Einfluss mehr.

    function objectMaker (id) {
	var private = 42; // bleibt erhalten nach der lex. Closure
	return {
	    "id" : id,	// <^ durch closure sind id und id gebunden
	    "toString": function () {
		return "[Object "+id+"]";
	    },
	    "valueOf": function () {
		return private.valueOf();	
	    }
	};
    }
    var o1 = objectMaker("doug");
    var o2 = objectMaker("crock");

Die Tatsache, dass man mit Closures, also der lexikalischen Schliessung ueber dem Inhalt der Funktionen, eine Menge Wunder vollbringen kann, weil man praktisch private Variablen halten kann, ohne sich die Objekte zu versauen, weil da ueberall Properties zugewiesen wurden, macht man sich hier auch zu nutze.

    // Moment, das ist glaube ich eine Wiederholung von oben,
    // da sollte ich das hinkopieren.
    var object_extender = function (object, extend) {
	var F = function(){};
	F.prototype = object;
	var that = new F();
	if (typeof extend === "object" && extend !== null) {
	    for (var p in extend) {
		if (extend.hasOwnProperty(p)) {
		    that[p] = extend[p];
		}
	    }
	}
	return that;
    };	

Im folgenden noch ein paar Beispiele, wo Funktionen dazu genutzt werden, Objekte zurueckzugeben. Sie dienen auch als Konstruktorfunktionen, allerdings muss man nicht mehr mit new arbeiten. Ausserdem wird this nicht verwendet, damit gibt es praktisch Idiotensicherheit in nicht strict mode oder ES3 Systemen.

    var coolConstructor = function () {
	var that = {};
	var privateVar = "cool";
	that.publicMethod = function () {
	    return privateVar;
	};
	return that;
    };

    // Idiotensicheres Pattern, wo new egal ist.
    
    var a = coolConstructor();
    console.log(a.publicMethod());
    var b = new coolConstructor();
    console.log(b.publicMethod());
    // cool
    // cool

Mann kann Objekte aus diesen Funktionen zurueckgeben, new hat keinen Einfluss, new kann man nicht brauchen.


    var defer = function () {
	var promise = {};
	var deferred = { promise: function() { return promise; } };
	return deferred;
    };
    
    var deferred = defer();
    console.dir(deferred.promise());

Mit dem Pattern kann man auf new verzichten.

    function Constructor () {
	var constructed = this;
	var args = arguments;
	if (!this instanceof Constructor) {
	    constructed = new Constructor();
	} else {
	    constructed.foo = function () {
	    };
	    
	    constructed.bar = function () {
	    };
	}
	if (args.length > 0) {
	    
	}
	return constructed;
    }

Namespacing

Mit Objekten oder Funktionen über Properties. Das liest sich dann wie ein Pfad.

Eingefallen ist mir das, als Addy Osmani in seinem EssentialJS Buch auch noch ein Namespacing Kapitel hat. Ich hatte es nicht Namespacing, sondern Pfad, genannt, wie man von Javapaketnamen kennt. Ich hatte mich damit auch schon beschäftigt. Darum hier mein eigenes Kapitel.

Namespaces als absolute URLs sind uebrigens absolut unique. Es gibt immer nur ein Verzeichnis unter einem Namen auf einer Festplatte. So sind die Strings dann natuerlich auch. Falls ihr noch einen AMD Loader schreiben wollt, merkt euch dass mal fuer define, require.toUrl(id) und die registry. Aber das ist jetzt OT. Zurück zum Namespacing mit Objekten

JavaScript hat keine namespace std { }; sowie using namespace std; oder std::cerr Statements wie zum Beispiel C++. Andere Programmiersprachen wie PHP oder Perl oder Java haben auch richtige Namespaces. Die URL von XML Namespace ist ein Identifier, der auch einen Uniquen Pfad darstellt, XML hat keinen namespace Wrapper mit Curly Braces. JavaScript hat sowas auch nicht.

In JavaScript kann man dennoch Namespaces erstellen...

Kennt ihr..

	YAHOO.Dom.Util.something
	
	// oder
	
	var map = new google.maps.Map();
    

..wie kriegt man das hin?

Antwort: Objekte. Jeder "Namespace" ist ein Objekt, angelegt als Property des Objekts, das den Namespace in dem der angelegt wird, beschreibt. Man kann diese Objekte praktisch Traversieren. Man koennte der Anstelle eines {}; eine function Namespace () nehmen

	
	LSWT = LSWT || {};	 // 1. Falls es das globale Objekt nicht gibt, anlegen.
	
	LSWT.space = {};	// 2. Das erste Unterobjekt des LSWT-Namensraums
	
	LSWT.space.name = "eddie";
	LSWT.space.test = function () { console.log(this.name); };
	LSWT.space.test2 = function () { console.log(LSWT.space.test2); };
	
	LSWT.room = {};
	LSWT.room.backdoor = {};
	LSWT.room.backdoor.garden = {};
	
	LSWT.room.backdoor.garden.flower = "rose";
	LSWT.room.backdoor.garden.tree = "oak";
	
	// Hier mal ein 
	
	LSWT.Main = function () {
	    this.name = "main";
	};
	
	// Jetzt einmal Achtung, dass ihr euch nicht vertut
	LSWT.Main.abc = function () {
	    console.log("Bin nur am Konstruktor");
	};
	var main = new LSWT.Main();
	
	// Jetzt die uebliche Falle: Die Funktion abc existiert nicht
	// Es ist so, dass new LSWT.Main ein neues Objekt zurueck gibt
	// Die Funktion abc ist am Constructor Main und wird dort aufgerufen
	
	try {
	    main.abc();
	} catch(e) {
	    console.log(JSON.stringify(e));
	}
	
	LSWT.Main.abc();
	
    

Mit einer namespace function und einer Objekt Id sind die Namespaces traversierbar. Auch wenn das wahrscheinlich keiner mehr machen wird, kam mir die Idee gerade. Zeigt auch nochmal, was man mit Object.toString alles machen kann.

	// Namespace Objekt mit add und toString Funktion
	// speichert seinen Namen und seinen Parent (traverse up ist sonst unmoeglich)
	
	function Namespace (name, parent) {
	    this._NS = name;
	    this._parentNS = parent || null;
	}
	Namespace.prototype.add = function add (name) {
	    this[name] = new Namespace(name, this);
	};
	Namespace.prototype.toString = function toString () {
	    return "[object Namespace]";
	};
	Namespace.prototype.traverse = function traverse (visit) {
	    var ns = this;
	    visit(ns);
	    Object.keys(ns).forEach(function(p) {
		if (typeof ns[p] === "object" && ns[p].toString() === "[object Namespace]") {
			traverse(ns[p]);
		}
	    });
	};
	
	// erzeuge ein paar Variablen
	
	LSWT = new Namespace("LSWT");
	LSWT.add("nsOne");
	LSWT.add("nsTwo");
	LSWT.nsOne.add("nsThree");
	LSWT.nsTwo.test = "pegel";
    
	// ausprobieren
	LSWT.traverse(function (ns) {
	    console.log(ns._NS);
	});
	
	LSWT.nsTwo.traverse(function (ns) {
	    console.log(ns.test);
	});
    

Wie man sieht, kann man mit JavaScript Konzepte des Namespacings mit Objekten simulieren und diese sogar um Ideen wie Traversion erweitern.

Namespaces wie in Actionscript

Brendan Eich hat 2009 bei Yahoo erzählt, warum ES4 2008 gescheitert war. Ein anderes Thema war, warum Namespaces wie bei Actionscript nicht kommen. Er zeigte dann, wie man das programmieren muesste, und dass das gar nicht so einfach waere. Bei Actionscript konnte man sich auf class verlassen, was eine Art eingefrorenes integres Objekt darstellte. Bei der Dynamik der Script Tags und des unterschiedlichen Codes, der im Browser läuft, ist das aber gar nichts so einfach, das zusammenzubasteln.

packages, wie man aus Actionscript oder Mozilla (package org.Mozilla.foo) kennt, oder nicht, wie ich, wurden damals auch wieder entfernt.

Iterator Patterns

Eine Interpretation von Design Patterns. Bei Gamma gibt es das Iterator Pattern, zum Beispiel einen Listiterator, der First, Next, Last, CurItem definiert.

In EcmaScript möchte ich die Funktionsbasierte Iteration als Iteratorpattern vorschlagen.

	function ArrayIteratorMap(array, callback) {
	    "use strict";
	    var results = []
	    for (var i=0, len=array.length;i<len;i++) {
		results.push(callback(array[i]));
	    }
	    return results;
	}
    
    
	var values = ArrayIteratorMap(["a", "b", "c"], function (item) {
	    return item.charCodeAt(0);
	});
	
	console.log(values);
	// [ 97, 98, 99 ]
    

Das verstehe ich zum einen unter Iterator Pattern in EcmaScript.

Anderseits kann man das auch so machen. Wie im Original.

(Die Funktionen sind jetzt dahingeschmiert und nicht ueberprueft, man soll das Muster erkennen.)

	function makeArrayIterator(array) {
	    "use strict";
	    var count = 0;
	    return Object.freeze({
		reset: function () {
		    count = 0;
		}
		next: function () {
		    ++count;
		    if (count > array.length-1) { 
			count = array.length - 1;
			 return; 
		    }
		    return array[count];
		},
		cur: function () {
		    return array[count];
		},
		at: function () {
		    return count;
		},
		first: function () {
		    return array[0];
		},
		last: function () {
		    return array[array.length -1];
		},
		run: function (callback) {
		    var results = []
		    for (var i=0, len=array.length;i<len;i++) {
			results.push(callback(array[i]));
		    }
		    return results;
		}
	    });
	};
    
	var iter = makeArrayIterator(["a","b","c"]);
	var vals = iter.run(function (item) {
	    return item.charCodeAt(0);
	});

	// vals ===  [97,98,99];
    
    

Ich denke mir, so kann man das interpretieren. Ich habe das Objekt jetzt etwas dahingeschmiert, das muss ich nochmal verbessern.

Avoid from ES3

Dinge die man heute nicht mehr verwenden sollte...

1. das with statement. Es ist einfach zu unsicher, weil man nicht weiss, welche Variable jetzt von wem wäre...in ECMA Script 5 strict mode wurde das with statement entfernt.

	var a = 3, 
	b = 4;
	var foo = { a: 1, b: 2 };
	with (foo) {
	    b = 32;
	    a = 12;
	}
    

Man kann jetzt eigentlich nicht mehr sagen, ob foo.a oder var a gemeint ist. Das gleiche gilt für b. Und in etwas komplexeren Programmabläufen führt das auf einmal zu Bugs..

---

Entschärfung des with "statement considered harmful" arguments:

Es folgt kein Beispiel, sondern eine Frage nach der Implementation des with-Statements. Ist es korrekt implementiert, ist voraussagbar, was geschieht. Solange ich das nicht nachgeschlagen habe, kann ich es nicht beantworten. Wenn with aber seinen Scope kreirt, wie ich meine ich schonmal in High Performance JavaScript gehoert zu haben, ist definitiv foo.a vor var a in der scope chain, weil with sein eigenes objekt als erstes einreiht.

Nicholas Zakas erklärt die Scope Chain in, ich glaube in, (High Performance oder eher) Speed Up Your JavaScript. Und ich glaube, dort sagt er sogar, dass with ein Objekt erzeugt. Aber ich meine dann auch, vorauszusagen zu koennen, ob var a oder foo.a das erste a in with(foo) wäre - foo.a.

Damit liesse sich schon entschaerfen, dass es verwirrend waere. Anderseits, ich habe nicht vor, dafuer nochmal Werbung zu machen. Keine Angst.

---

switch

Das switch-case-default statement ist eine alternative zu if. In JavaScript ist der Typ, der auf der linken Seite ein Zeichen, eine Number, oder ein String. Arrays oder Objekte gehen nicht. In C ist es ein char oder Zahlenwert.

	function next() { return "Edward"; }
	switch (next()) {
	    case "Edward": 
		console.log("Richtig");
		break;
	    default: break;
	}
	// Richtig
    

Man muss nach einem Case Statement immer break schreiben, sonst fällt man zum nächsten case durch..

	function next() { return "Edward"; }
	switch (next()) {
	    case "Edward": 
		console.log("Richtig");
	    case "Gerhold": 
		console.log("Durchgefallen!");
	    default:
		console.log("durchgefallen!");
	}
	// Richtig
	// Durchgefallen!
	// durchgefallen!
    

Douglas Crockford hat einmal eine interessante Geschichte zum switch Statement erlebt. Er unterhielt sich gerade mit jemandem darüber und gab ihm advice. Dieser jemand kam wieder und meinte, er fand einen Bug in JSLint. Ich denke mal, auch er dachte erst, Lamer. Dann stellte sich heraus, das Douglas in einem switch case durchgefallen war. Der Mann hatte recht. Seitdem erzählt Douglas die Geschichte. Ich glaube in "Programming Style and your Brain".

Arrays oder Objekte gehen nicht...

	switch ([]) {
	    case []: 
		console.log("Array");
		break;
	    default:
		console.log("Oh God!");
		break;
	}
    
	// Oh God!
    

Regulaere Ausdruecke gehen auch nicht...

	switch ("a") {
	    case /a/: 
		console.log("a");
		break;
	    default:
		console.log("Oh God!");
		break;
	}
    
	// Oh God!	
    

Dieses Kapitel ist besonders trivial und vielleicht interessant, mal Worker, Browser oder die Umgebung von Node zu benennen, aber nach ein paar Monaten Übungen, finde ich den Eintrag aus den ersten Tagen des Doks doch etwas komisch. -- Anmerkung fuer den Editor, mich.

Umgebungen

Worker, Browser, Node

Kurz: JavaScript ist auf dem Weg in (praktisch) alle Geräte.

JavaScript kann heutzutage in verschiedenen Umgebungen laufen. Die gängigsten drei Umgebungen, Browser, Worker*, Server, und ihre Besonderheiten seien hier kurz erläutert.

*eigtl. nur ein Prozess im Browser oder Server. Worker bezeichnet hier den Web Worker als Besonderheit im Browser. Auf dem Server gibt es Worker Threads, Prozesse und sogar Concurrency (Rhino).

(Zur Zeit hat der der das schreibt noch keine Ahnung, weil er selbst ja gerade erst damit anfaengt. Jedenfalls gibt es eine Vielzahl Serverplattformen auf denen JavaScript laeuft. Und verschiedene Devices. Und dann halt die drei Umgebungen hier.)

Browser

Im Browser gibt es ein Window Objekt. Das Window Objekt speichert alle globalen Variablen.

Ausserdem enthät Window das document. Und entgegengesetzt besitzt ein DOM document einen defaultView, ein Window Objekt. Jeder Browsertab hat ein eigenes Window Objekt.

Ein Iframe hat ein eigenes Window Objekt und ein eigenes Document.

Web Worker

Im Web Worker gibt es kein Window Objekt. Anstelle von Window gibt es eine Referenz auf den Worker. self genannt.

Im Web Worker gibt es nicht nur kein Window. Nein, auch kein Document. Das heisst, auch kein document.getElementById() oder document.querySelectorAll() oder einen HTML- oder XML-Baum

	// filename.js
	// hier gibt es kein window oder objekt
	// aber XMLHttpRequest und importScript
	self.onmessage = function (e) {
	    if (e.origin == "http://127.0.0.1") {
	        if (e.data === "start") {
		// run the first function
		}
	    }
	};
	self.postMessage("Hallo Webfan!", "http://127.0.0.1");
     
        // webapp.js
	var worker = new Worker("filename.js");
	worker.onmessage = function (e) {
	    // received from filename.js e.data
	    console.log("Der Worker sagt: " + e.data);
	};
	worker.postMessage("start", "http://127.0.0.1");
    

Ein Webworker ist datengetrieben. Er erhaelt Daten über ein Message Event im Workerscript self.onmessage = function(e) { /* e.data */ };. Und er schickt Daten mit einem Message Event durch postMessage(data); zurueck, die im Browserscript bei der Workervariable worker.onmessage ankommen.

Einen InlineWorker, in dem das document und window da waeren, habe ich noch nie gesehen, ich glaube der existiert mehr auf Blatt Papier.

Server (node.js)

Unter Node gibt es kein Window Objekt.

Die globalen Variablen gehen in das Object global.

Jeder Node Prozess hat ausserdem ein process Objekt.

Hier ein Skript was man als Modul laden kann, und was, wenn es nicht als Modul geladen wird, sondern mit node script.js ausgefuehrt wird, und dann kein parent Modul hat, eine Startfunktion ausfuehrt.

    module.exports.myFunction = function () {
	for (var i=0, j=arguments.length; i<j; i++) {
	    console.log("argument "+i+"= "+arguments[i]);
	}
    };
    
    if (!module.parent) {
	exports.myFunction.apply(null, process.argv);
    }
    
    
    // Variante 1 (als Modul):
    linux-qc69:~ # node
    > var myFunction = require('./script.js').myFunction;
    > myFunction("a","b","c");
    argument 0= a
    argument 1= b
    argument 2= c
    
    // Variante 2 (als Skript):
    linux-qc69:~ node script.js
    node
    script.js

Bei Variante eins ist der REPL das Top-Level Modul und module.parent !== null. Bei Variante zwei gibt es kein Modul, was vorher geladen ist, und module.parent === null. module ist eine freie Variable im CommonJS System, was node ist.

Node hat evented I/O, node hat ein tickbasiertes Event-Loop System, einen Read-Eval-Print-Loop, in dem man von Hand JavaScript eingeben kann und eine tolle Netzwerk und Filesystem API mit URL, Querystring und Pfad Parsern.

Node hat ein sehr reichhaltiges Netzwerk API was einem ermoeglicht, tolle Server zu schreiben, hat bereits fertige Frameworks und ueber 10,000 brauchbare Pakte.

JavaScript Engines

Jeder Browserhersteller hat seine eigene Engine, oder sie werden in diversen Projekten verbaut. node.js zum Beispiel basiert auf Googles V8 Engine, wie auch der Chrome Browser. SpiderMonkey (libmozjs185) ist zum Beispiel die Basis für den Firefox.

C/C++ Programmierung

Das ist richtig, das Programmieren der Engines erwartet etwas C/C++ Kenntnisse. Das ist aber alles gar nicht so schwer, man muss sich nur merken koennen, wann auf eine Adresse gezeigt wird, oder wann man ab einer Adresse liest (das sind diese Pointer, die C/C++ noch haben), aber die Libraries der Engines sind so gestaltet, dass man flüßig mit ein und dem gleichen Stil programmieren kann, pro Engine versteht sich.

Wenn man im Internet sucht, findet man auf jeden Fall Tutorials und Referenzen, um reinzukommen, und ausserdem gibt es die .h Header Dateien. Es muss nicht immer alles am gleichen Tag passieren, dann klappt das schon.

ECMAScript next ???

Wo kann man die Features testen?

Im Mozilla kann man die neueste JavaScript Version next ausprobieren. Sagt man so, ist mir bislang aber nicht gelungen.

	    <script type="application/javascript;version=next"></script>
	

Die Experimental JavaScript flags chrome://flags sollen das gleiche ermöglichen, funktionierte bei mir beim letzten Test aber nicht.

Event Loop

Browser

    
    var stop = false;
    function demo () {
	console.log(".");
	if (!stop) setTimeout(demo, 0); // 0ms enqueued ihn 
    }
    
    demo();
    // ...... bis jemand stop = true eingibt;
    stop = true;
    // true
    // Punkte hoeren auf zu zeichnen.
    

Auf NodeJS kann man dem Event Loop einfach mit process.nextTick eine neue Funktion enqueuen.

	var stop = false;
	function demo() {
	    console.log(".")
	    if (!stop) process.nextTick(demo);
	};
	demo();
	// ........ bis jemand stop = true schreibt.
	stop = true;
	// .. punkte hoeren auf
    

Falsch ist es, die Funktion direkt zu rufen. Sie wird nicht nur unmittelbar aufgeruffen. Der Callstack wird immer grösser.

Irgendwann sind zuviele Aufrufe von wrong auf dem Stack und ein RangeError fliegt. Das Programm stuerzt dann an ab.

function wrong () {
    // Der direkte Aufruf blockiert den ganzen Browser
    // bis der Callstack ueberlaeuft.
    if (!wrong.stop) wrong();
}
wrong.stop = false;
wrong();
// RangeError: Maximum call stack size exceeded
// Die folgende Funktion wird gar nicht erst ausgefuert
setTimeout("console.log(' can not influence wrong()'); wrong.stop = true", 200);

Der JavaScript Singlethread ist hier mit der Rekursiven Funktion beschaeftigt, bis der Call Stack so gross ist, dass abgebrochen wird.

Und jetzt nochmal, wie man es richtig macht. Mit setTimeout(f, 0) wird die Funktion nach der Sequenz (der aktuellen Funktion, die erstmal abgearbeitet wird) eingereiht, oder, wenn der Wert groesser als 0ms ist, in der Zukunft erst ausgefuehrt.



function right () {
    console.log(".");
    if (!right.stop) setTimeout(right, 0);
}
right.stop = false;
right();

// Die Funktion wird ausgefuert, weil setTimeout richtig enqueued.

function stopRight () {
    console.log('can influence right()');
    right.stop = true;
}

// Die Funktion right zeichnet 200ms lang Punkte, dann wird diese Funktion hier gerufen
// und unterbricht sie, weil right.stop auf true gesetzt wird und der naechste
// setTimeout von right nicht mehr erfolgt.

setTimeout(stopRight, 200);

// .
// .
// .
// can influence right()
// .

Zum Schluss kann man nochmal wunderbar sehen, wie das enqueueing funktioniert, und erst die Sequenz (die aktuelle Funktion) beendet wird, bevor der naechste Timeout aus dem Queue ausgefuehrt wird. process.nextTick(f) auf node.js liefert das gleiche Ergebnis.

function testTimeout () {
    console.log("test");
    console.log("test");    
    console.log("test");    
    console.log("test");
    setTimeout(function () { console.log('timeout'); }, 0);
    console.log("test");    
    console.log("test");
    console.log("test");    
    console.log("test");    
}
testTimeout();

// test
// test
// test
// test
// test
// test
// test
// test
// timeout

Events

"JavaScript ist ereignisgetrieben"

Nett gesagt. Genaugenommen war es eine Idee, von Brendan Eich, Bill Atkinsons Hypercards Eventhandler zu implementieren. Doug Crockford hat mir die Hypercard Geschichte in seinen Vorträgen erzählt, und Brendan Eich auch in einem Interview, dass ich das verstanden habe.

Ürsprünglich sind die on-Handler von Hypercard, was es in den 80ern auf Macintosh gab, was erfolgreich war, weil selbst Menschen, die nicht programmierten, plötzlich intelligente Stacks programmierten, inspiriert.

period.

Ereignisse sind natürlich in der gesamten Bandbreite an Software vertreten, die immer wieder in Variation oder in bekannten Patterns (Observer-Notifier, Listener-Event) auftauchen.

"Auf dem Server und dem Client" gibt es verschiedene Namen für die Funktionen, aber das Prinzip ist überall gleich.

Ein Berechnung ist fertig, oder fängt gleich an. Das Objekt schickt eine Botschaft. Oje, da fallen mir die Sequenzdiagramme direkt wieder ein. Eine Botschaft schickt es. (Unter andere, ist Botschaft rufen auch nur ein anderer Name fuer Funktion rufen

Die Geschichte vom Actor Model und den postMessages erzaehle ich demnaechst nochmal, dafuer werde ich sie nochmal recherchieren und mir beibringen.

(Dass ich solche unsachlichen Abschnitte in dem Text habe... Oje! Wird Zeit sich mal wieder alles durchzulesen und grosszuegig zu korrigieren. Ich habe ja genug Inhalt zusammengestellt, dass ich das Dokument damit nicht gleich ausloesche, ;-))

/*
* EventTarget im DOM
* Wenn eine Berechnung fertig ist,
* erst ein Event Objekt erzeugen. Dann dispatchen.
*
* var event = new Event("ende_berechnung");
* eventtarget.dispatchEvent(event);
*
* Dennoch:
* Das ist hier doch noch falsch (off-spec meint er), man kann hier ebenso mit 
* dispatchEvent("ende_berechnung", data) dispatchen.
*
* EventEmitter kommen ohne Objekte aus,
* die hier muss man erst erzeugen, dann dispatchen.
*
* Off-Spec: Bubblen und capturen gehen nicht parentNode rauf childNodes runter
* (Man berechnet von target aus die parentNodes nach oben, macht einen Array
* raus, und captured die runter, geht at_target und bubblet die wieder hoch.
* Der Propagation Path wird aus "while (node=node.parentNode) path.push(node);"
* berechnet. Das muss hier noch verbessert werden
* Hier sind addTarget und ein Array dran, weil JavaScript Objekte kein Parent
* haben, jedenfalls kein standardisiertes Parent, was man erreichen kann.
* Wenn ich ein Objekt als Property eines Objekts habe, komme ich nicht an
* das Objekt dran, was die Property hat. Darum muss man mit addTarget manuell
* was zuschreiben. In C++ und mit der Engine ist das anders. Da gibt es 
* Funktionen um Parent zu ermitten (in Spidermonkey z.B. JS_GetParent und JS_SetParent)
*/


function Event (type) {
    if (this instanceof Event === false) 
	return new Event(type);
    if (typeof type !== "string") 
	throw new TypeError("new Event(type): string argument expected");
    this.type = type;
    this.initialized = false;
    this.canceled = false;
    this.timeStamp = Date.now();
    this.isTrusted = false;
}

/* Achtung: Hier mache ich was falsch:

 Der Prototype ist SHARED */

Event.prototype.constructor = Event;
Event.prototype.canceled = false;
Event.prototype.target = null;
Event.prototype.currentTarget = null;

/*
 ALLE BOOLS und FLAGS duerfen NICHT (!!!)
 AUF den PROTOTYPE. Das habe ich hier noch 
 nicht gewusst oder beachtet und das waer
 dann spaeter bei vielen asynchronen Events
 im Queue aufgefallen, dass die Flags falsch
 sind oder vom letzten...
 
 Der Prototype ist shared
 und 
 
 canceled 
 target
 currentTarget
 eventPhase
 preventedDefault
 returnValue
 bubbles
 
 duerfen NICHT auf den Prototype, sie werden
 im Constructor das erste mal erzeugt, damit
 jeder eine eigene Variable hat,
 
 wenn man nicht aufpasst kann man sich hier
 mit "prototypal inheritance" vertun.
 

 
 in function Event (type) { // oder in initEvent (lest die Spec, aber nicht auffm prototype)
    this.eventPhase = null; 
    this.bubbles = false;
    this.eventPhase = null;
    this.preventedDefault = false;
    this.returnValue = null;
 }

*/

Event.prototype.initEvent = function (type, bubbles, cancelable, data) {
    Event.call(this, type);
    // Das ist nicht vollstaendig
    this.initialized = true;
    this.bubbles = bubbles || false;
    this.cancelable = cancelable || true;
    this.data = data || null;

};
Event.prototype.eventPhase = null;
Event.prototype.preventedDefault = false; // FALSCH: diese Flags duerfen nicht auf den Prototype
Event.prototype.preventDefault = function () { // RICHTIG: Die Methode darf auf den Prototype
    this.preventedDefault = true;		// Sie kann geshared werden, die Property preventedDefault hingegen nicht
};
Event.prototype.stopPropagation = function () {
    this.cancelBubble = true;
};
Event.prototype.stopImmediatePropagation = function () {
    this.canceled = true;	// Kann sein, dass ich hier was falsch mache.
    this.cancelBubble = true;
};
Event.prototype.dispatch = function (target, data) {
    data = data || this.data;
    target.dispatchEvent(this, data);
};
Event.CAPTURING_PHASE = Event.prototype.CAPTURING_PHASE = 1;
Event.AT_TARGET = Event.prototype.AT_TARGET = 2;
Event.BUBBLING_PHASE = Event.prototype.BUBBLING_PHASE = 3;

/* Das ist alles nicht brauchbar..
Event.CHANGE = Event.prototype.CHANGE = 32768;
Event.CLICK = Event.prototype.CLICK = 64;
Event.DBLCLICK = Event.prototype.DBLCLICK = 128;
Event.DRAGDROP = Event.prototype.DRAGDROP = 2048;
Event.FOCUS = Event.prototype.FOCUS = 4096;
Event.KEYDOWN = Event.prototype.KEYDOWN = 256;
Event.KEYPRESS = Event.prototype.KEYPRESS = 1024;
Event.KEYUP = Event.prototype.KEYUP = 512;
Event.MOUSEDOWN = Event.prototype.MOUSEDOWN = 1;
Event.MOUSEUP = Event.prototype.MOUSEUP = 2;
Event.MOUSEOVER = Event.prototype.MOUSEOVER = 4;
Event.MOUSEOUT = Event.prototype.MOUSEOUT = 8;
Event.MOUSEMOVE = Event.prototype.MOUSEMOVE = 16;
Event.MOUSEDRAG = Event.prototype.MOUSEDRAG = 32;
Event.SELECT = Event.prototype.SELECT = 16384;
*/

Das ist jetzt nicht perfekt oder vollstaendig, aber (voll) funktionsfaehig. Aber das ist ein kompliziertes Event Objekt aus dem DOM. Eine Implementation ohne Event Objekt am Ende des Kapitels. Schaut nach Emitter, den ich an node.js angelehnt programmiert habe.

Ein Event wird geschickt...

Andere Objekte, die sich mit einem Listener für das Event registriert haben, bekommen ihre Listener Funktion nun der Reihe nach aufgerufen. Dabei wird das Ereignisobjekt mit möglichen Daten, dem aktuellen Zustand, übergeben.

    function handleEvent(e) {
    	console.log( e.target === window ); // true
	console.log( e.data ); // "Hallo"
    }
    window.addEventListener("click", handleEvent, false);
    window.dispatchEvent("click", "Hallo"); 			// oder event.dispatch(window); nachdem man ein Event erzeugt und initEvent gerufen hat.
    window.removeEventListener("click", handleEvent);
	

Hier wird ein EventListener fuer einen Klick angebracht, das Event einmal dispatched und dann der Listener wieder removed, der auf der Console ausgibt, ob e.target === window ist. Da ich "Hallo" als Data übergeben habe, ist e.data === "Hallo";

Im DOM durchlaeuft das Event mehrere Phasen...

CAPTURING_PHASE 1, das Event durchlaeuft den Weg vom Window bis zum Target hin, und sofern der Listener das Event capturen kann, werden die Listener fuer das Event bis dorthin aufgerufen.

			event.target = this;
			event.data = data;

			event.eventPhase = event.CAPTURING_PHASE;
			if (targets && event.capture) {
				capturing: for (i = targets.length - 1, j = 0; i >= j; i--) {
					if (event.stoppedPropagation) {
						break capturing;
					}
					target = event.currentTarget = targets[i];
					listeners = target._eventListeners[event.type];
					if (listeners === undefined) {
					    continue capturing;
					}
					len = listeners.length;
					if (len === 0) {
					    continue capturing;
					}
					for (k = 0; k < len; k++) {
						listener = listeners[k];
						if (listener.capture) { // falls es ein caputuring listener ist
						    cancel = listener.call(target, event) ? false : true;
						}
						if (cancel) { 			// sichere zu, dass die Variable nur einmal gesetzt wird, wenn und dann so bleibt
						    event.canceled = true; // wuerde ich das eine Zeile drueber zuweisen wuerde es jedesmal ueberschrieben werden. 
						}
					}
				}
			}
	

Der dritte Parameter der addEventListener(typeString, callbackFunc, captureBool) Funktion ist dafür gedacht - ABER - kein Browser unterstützt das wohl bis heute. Oder doch? Ich hab das noch nicht optimiert. Das sieht nur aus wie ein DOM Event, ist aber noch schlecht programmiert. Wenn ihr jetzt vorwarts statt rueckwaerts zaehlt, habt ihr Bubble. Und da AT_TARGET nur einmal vorkommt, kann man event.canceled dort direkt zuweisen.

Je nachdem ob gecanceled wurde oder nicht, ruft man die Default Operation, oder die, die alles regelt, wenn was gecanceled wurde, wenn die Software so hoch entwickelt ist, dass das noetig ist.

AT_TARGET 2. Das Element, dass das Event ausgeloest hat. Was in der Regel auch e.target ist.

Nach dem Element, ist das EventTarget benannt.

	    event.eventPhase = event.AT_TARGET;
	    target = event.currentTarget = event.target = this;
	
	    // rufe alle _eventListeners[event.type]
	

BUBBLING_PHASE 3. Das Event durchlaeuft alle Elternknoten bis zum Window rauf. CurrentTarget ist === dem Knoten, e.target bleibt das Original AT_TARGET

	    event.eventPhase = event.BUBBLING_PHASE;
	    bubbling: for (i = 0, j = targets.length; i < j; i++) { // alle registrierten targets lang, einmal alle listeners fuer das event rufen
		target = event.currentTarget = targets[i];
		listeners = target._eventListeners[event.type];
		// code wie oben
		// falls listener vorhanden
		// falls nicht capturing handler ist, ausfuehren
	    }
	

Bei Events die Bubblen (oder man capturen kann) kann man bequem einen Listener an den Elternknoten haengen und gucken, ob e.target === dem gesuchten Kindsknoten ist (Event Delegation).

Die element["on" + type] Handler, die "on-Funktion", fuehre ich als primaeren Handler aus.

Das sind die Funktionen, die von Hypercard beeinflusst waren... :-)

	onHandler = target["on" + event.type];
	if (onHandler && typeof onHandler === "function") {
	    listeners = [ onHandler ].concat(listeners);  
	} else if (onHandler && typeof onHandler === "string") {
	    listeners = [ new Function("e", onHandler) ].concat(listeners);
	}
	

Im HTML kann das ja auch ein String sein, darum fuehrt onxxx Funktionen und Strings aus.

	meineLib.onmessage="JavaScript:console.log('Das geht wirklich, weil der Interpreter JavaScript: auch ausserhalb von html parsen kann.');";
	

Haengt man zum Beispiel einem DIV in dem eine UL steckt einen click-Listener an und klickt auf ein LI, ist das Event Target direkt das geklickte LI. Ob es das richtige ist, auf das man den Befehl ausfuehren will, muss man dann noch ueberpruefen.

Das Event ist vom Kind losgeschickt worden und bis zum DIV hochgebubblet. Mit dem Listener schaut man sich nochmal das Event Target, was das Event ausgeloest hat, an.

Event Target sollte immer das Target sein, was das Event wirklich ausgeloest hat.

/*
* Naja, als ich das programmierte, habe ich gerade mal 4 Wochen lang
* JavaScript gemacht. Der Code ist also noch nicht so gut und ganz genau
* genommen, habe ich die DOM Spec nur kurz ueberflogen und dann wieder
* weggelegt. Das Ding funktioniert bereits. Muss also nochmal gemacht werden.
*/
	

/*
* Event Target, 
* extende dein Objekt von Event Target oder mixe
* es in und apply den Constructor auf dem Objekt
* und du hast ein DOM aehnliches Event System.
*/


var EventTarget = function () {
    if (!this._eventListeners) {
        this._eventListeners = Object.create(null);
        Object.defineProperty(this, "_eventListeners", {	// bringe die Listener NIEMALS auf dem Prototype, sonst teilen sich mehrere Objekte die Property (mehr dazu unten beim Emitter)
            enumerable: false
        });
    }
}
    
EventTarget.prototype.addEvent = function (type, defaults) {	// Die Methode addEvent ist zuviel (oder halt sehr strict)
    if (!this._eventListeners) {				// Das Dom verlangt nicht die Initialisierung eines Events 
        this._eventListeners = Object.create(null);		// Sondern zum Dispatch ein erzeugtes Objekt
        Object.defineProperty(this, "_eventListeners", {	// Hier liege ich vor vier Monaten im Irrtum
            enumerable: false					// Als ich addEvent und addEventTarget (Propagation Path)
        });							// erfinde. AddEventTarget ist super. Aber addEvent zuviel,
    }								// weil erst eventtarget.addEvent("berechnung") initialisiert
    this._eventListeners[type] = [];				// werden muss. Hier hab ich mich leider vertan.
    if (event instanceof Event || (event instanceof Object && event.type)) {
        this._eventListeners[type].event = event;
    } else if (typeof type === "string") {
        this._eventListeners[type].event = new Event(type);
        this._eventListeners[type].event.initEvent(type, false, false, defaults);
    }
    var event = this._eventListeners[type].event
    if (typeof defaults === "object" && defaults != null) {	// Hier wird die default Operation des Events, die man mit 
        if (defaults.capture) event.capture = defaults.capture;			// e.preventDefault verhindern kann abgespeichert.
        if (defaults.defaultOperation) event.defaultOperation = defaults.defaultOperation;	// Ich denke auf meine Art
        if (defaults.cancelOperation) event.cancelOperation = defaults.cancelOperation;	// als ich mit dem Programmieren anfing
        if (defaults.bubbles) event.bubbles = defaults.bubbles;			
        if (defaults.cancelable) event.cancelable = defaults.cancelable;
        this._eventListeners[type].defaults = defaults;				// Ich denke mal hier habe ich noch einen Tag Arbeit vor mir
    }										// Das in Einklang zu bringen
    this._eventListeners[type].event = event;
    if (!events[type]) {
        events[type] = [];
    }
    events[type].push(this);
    return this;
};
EventTarget.prototype.addEventTarget = function (type, object) {		// Die Targets die man hier addet (alles EventTargets, bitte)
    if (typeof type !== "string" || typeof object !== "object" || 		// werden beim Bubblen und beim Canceln beruecksichtigt.
	typeof object._eventListeners !== "object") {			// Hier laueft das nicht so, dass ich das Objekt traversiere und 
	    throw new TypeError("addEventTarget: expecting event name plus an EventTarget object for the propagation list");
	}							// gucke ob die Property auch ein EventTarget Objekt ist und ich da dispatche
								    // Nein, hier muss man die Targets manuell adden
    var binding = this._eventListeners[type];
    if (binding) {
        if (!binding.targets) {
            this._eventListeners[type].targets = [];
        }
        this._eventListeners[type].targets.push(object);
    }
    return this;
};
EventTarget.prototype.addEventListener = function (type, callback, capture) {		// Das ist die beruehmte addEventListener Funktion
    if (!this._eventListeners) {
        this._eventListeners = Object.create(null);					// Was man hier sieht ist
        Object.defineProperty(this, "_eventListeners", {				// viel Boilerplate, dass ich die Listener Property
            enumerable: false								// (im DOM unsichtbar in C++ [secure] implementiert)
        });										// checke.
    }
    if (!this._eventListeners[type]) 
        this._eventListeners[type] = [];
    if (capture) callback.capture = true;
    else callback.capture = false;
    this._eventListeners[type].push(callback);						// callback.capture ist eine Property am listener.
    return this;									// ich koennte auch eine struktur speichern statt so
};
EventTarget.prototype.removeEventListener = function (type, callback) {
    var i, f, l;
    if ((!this._eventListeners) || (!type)) return;
    if (!this._eventListeners[type]) return;
    l = this._eventListeners[type].length;
    if (callback) {
        for (i = 0; i < l; i++) {
            f = this._eventListeners[type][i];
            if (f === callback) {
                this._eventListeners[type][i].splice(i, 1);				// hehe, ich glaube ich habe es hier das zweite mal gemacht
                l -= 1;
                i -= 1;									// das schreibe ich aber jetzt viel besser
            }
        }
    } else {
        this._eventListeners[type] = [];
    }
};
EventTarget.prototype.dispatchEvent = function (type, data) { // dispatch mit String
    var event, i, j, k, that = this;
    var listeners = [],
        listener, len, i, canceled, onHandler, targets
    if (!this._eventListeners) return;
    if (type instanceof Event || type instanceof Object) { // dispatch mit Objekt
        event = type;
        type = event.type;
    }
    if (!this._eventListeners[type]) {
        return;
    }
    event = event || this._eventListeners[type].event;	// <- Das ist nicht DOM, ausserdem ein "Default Objekt" (function addEvent(type))
    if (!event) return;
    event.eventPhase = null;
    event.currentTarget = null;
    event.preventedDefault = false;
    event.canceled = false;
    event.cancelBubble = false;
    event.timeStamp = Date.now();
    event.target = this;
    event.data = data;
    targets = this._eventListeners[type].targets;
    event.eventPhase = event.CAPTURING_PHASE;					// oja, die capturing phase
    if (targets && event.capture) {
        capturing: for (i = targets.length - 1, j = 0; i >= j; i--) {			// wie im Dokument beschrieben.
            if (event.cancelBubble) 
        	break capturing;
            if (target._eventListeners) listeners = target._eventListeners[event.type];
            if (listeners === undefined) continue capturing;
            len = listeners.length;
            if (len === 0) continue capturing;
            for (k = 0; k < len; k++) {
                listener = listeners[k];
                if (listener.capture) 
                    canceled = listener.call(target, event) === false ? true : false;
                if (canceled) 
            	    event.canceled = true;
                if (event.canceled)
            	     break capturing;
            }
        }
    }
    if (!event.cancelBubble && !event.canceled) {
        event.eventPhase = event.AT_TARGET;
        target = event.currentTarget = event.target = this;
        listeners = target._eventListeners[event.type];
        if (listeners === undefined) 
            listeners = [];
        onHandler = target["on" + event.type];
        if (onHandler) {
            if (typeof onHandler === "function") {
                listeners = [onHandler].concat(listeners);
            } else if (typeof onHandler === "string") {
                listeners = [new Function("e", onHandler)].concat(listeners);
            }
        }
        len = listeners.length;
        if (len > 0) {
            at_target: for (k = 0; k < len; k++) {
                listener = listeners[k];
                event.canceled = listener.call(target, event) === false ? true : false;
                if (event.canceled) 
            	    break at_target;
            }
        }
    }
    if (!event.cancelBubble && !event.canceled) {
        event.eventPhase = event.BUBBLING_PHASE;
        if (targets && event.bubbles) {
            bubbling: for (i = 0, j = targets.length; i < j; i++) {
                if (event.cancelBubble) 
            	    break;
                target = event.currentTarget = targets[i];
                listeners = target._eventListeners[event.type];
                if (listeners === undefined) 
            	    listeners = [];
                onHandler = target["on" + event.type];
                if (onHandler) {
                    if (typeof onHandler === "function") 
                	listeners = [onHandler].concat(listeners);
                    else if (typeof onHandler === "string") 
                	listeners = [new Function("e", onHandler)].concat(listeners);
                }
                len = listeners.length;
                if (len > 0) {
                    for (k = 0; k < len; k++) {
                        listener = listeners[k];
                        if (!listener.capture) 
                    	    canceled = listener.call(target, event) === false ? true : false;
                        if (canceled) 
                    	    event.canceled = true;
                        if (event.canceled) 
                    	    break bubbling;
                    }
                }
            }
        }
    }
    if (((event.canceled === undefined || event.canceled === true) || event.preventedDefault) && event.cancelable) {
        if (event.cancelOperation) event.cancelOperation.call(this, event);
    } else if (event.canceled !== undefined && event.canceled === false || event.cancelable === false) {
    // Der Name event.defaultOperation steht auch nicht so im DOM, aber was das ist, ist ja wohl klar, oder?
    // Die Defaultoperation, zum Beispiel die Navigation bei einem Click auf einen Href, die man auch unterdruecken kann
    // das ist die event.defaultOperation, die mein TargetMaker am Event Objekt festmacht.
        if (event.defaultOperation) event.defaultOperation.call(this, event);
    }
    return this;
};


	

event target Zum Bild. Ich hab das jetzt mit Copy + Paste kopiert. Der Witz ist, es dispatcht gerade ein new Event, was ein DOM Event Objekt sein muesste. Weil ich das Event von Oben noch nicht kopiert hatte. Ich denke mal, die Bubble und Capture Phasen sind da noch inkompatibel, weil ich nicht das cancelBubble Flag sondern die eigenen stoppedPropagation Variable nehme (geaendert - aber nicht probiert- wenn jetzt was nicht geht- dann weil ich stoppedPropagaten und stoppedImmediatePropagation in cancelBubble und canceled geaendert habe und canceled _wird_ falsch sein) und so gering von dem Original im DOM abweiche. Kann aber auch sein, dass ich das Event Objekt so veraendern kann, dass ich da meine Properties lese und schreibe. Ich bin jetzt zu faul den Rest zu probieren. Ich muss das Target nochmal neu schreiben.

Das war wieder vor einigen Wochen. Eddie streng dich mal an und mach sowas, das ist wichtiger, Events sind das Ding, mal ernster.

Was mir da aber noch auffaelt. Die addEvent Funktion um explizit ein Event zu deklarieren ist vielleicht zu strikt. Auf node.js muss man events nur Namen geben und das ist komfortabler. Ich bin da etwas von DOM abgewichen.

Bei einer eigenen Implementation kann man die Reihenfolge für capture und bubbles selbst bestimmen.

	EventTarget.prototype.addEventTarget: function (type, object) {
			var binding = this._eventListeners[type];
			if (binding) {
				if (!binding.targets) {
					this._eventListeners[type].targets = [];
				}
				this._eventListeners[type].targets.push(object);
			}
			return this;	// chainable function
	};
	

Hier meine Variante der window.postMessage Funktion. Ich habe sie mit dem EventTarget nachempfunden. Das Funktioniert jetzt mit normalen JavaScript Objekten. Man kann Targets hinzufuegen, und wieder entfernen. Im DOM hat postMessage eigene Regeln, wer dispatcht wird.

	function MessageTarget () {
	    this.addEvent("message", {	
		bubbles: true,
		capture: false,
		cancelable: false,
		defaultOperation: null,
		cancelOperation: null
	    });
	}
	MessageTarget.prototype = new EventTarget();
	MessageTarget.prototype.constructor = MessageTarget; 
	MessageTarget.prototype.subscribeMessages = function (node) {
		this.addEventTarget("message", node);
		return this;
	};
	MessageTarget.prototype.unsubscribeMessages = function (node) {
		var targets = this._eventListeners["message"].targets, 
		corrected = [];
		targets = targets.forEach(function (n) {
			n !== node ? corrected.push(n) : "";
		});
		this._eventListeners["message"].targets = corrected;
	        return this;
	};
	MessageTarget.prototype.postMessage = function (msg) {
		this.dispatchEvent("message", msg);
		return this;
	};
	

Ab hier hat das EventTarget eine postMessage Funktion und andere Objekte koennen sich bei diesem Objekt registrieren oder registriert werden. Deren onmessage und message Handler werden dann ausgefuehrt. Ich glaube alle als Bubble, wie man sieht. Capture ist false. :-) (Wer beides will, capture auf true setzen.)

Event Delegation - wie man Event Bubbling (auch Capturing) ausnutzen kann.

	    <ul>
		<li>1</li>
		<li>2</li>
	    </ul>
	
	    var clickHandler = function (e) {
		/* 
		// alter ie fix, der kein f(e) sondern window.event kennt 
		  e = e || event;
		  var target = e.target || e.srcElement; // bzw. srcElement statt target
		*/
		console.log("Geklickt wurde: " + e.target.innerHTML); // 1 oder 2 kommt raus
	    };
	    // entweder
	    
	    // onclick (beide)
	    document.querySelector("ul").onclick = clickHandler;
	    
	    // w3c
	    document.querySelector("ul").addEventListener("click", clickHandler, false);
	    
	    // internet explorer (hab ich nicht, keine Garantie fuer irgendwas)
	    document.querySelector("ul").attachEvent("onclick", clickHandler, false);


	    // node.js:
	    // npm install jsdom fuer DOM Events oder native Events:
	    // o.addListener(...) oder o.on(eventType, callback)
	    
	

Anstatt dass ich jetzt jedem LI einen Eventhandler verpasse, setze ich einen Eventhandler am Top-Level Element. Wenn ich auf das LI klicke, wird e.target (e.srcElement) auf LI gesetzt, und zwar das, was ich geklickt habe. Dann bubblet das Ding hoch und e.currentTarget wird UL, e.target bleibt aber LI und wir koennen eine Ebene hoeher lesen, wer das war.

Bei Events die nicht bubblen geht das leider nicht.

Event Emitter

Anders als im DOM mit Event und EventTarget, gibt es auf node.js einen EventEmitter. Dem nachempfunden habe ich ein einfaches Objekt Emitter programmiert, was man per copy und paste auch im Browser probieren kann. Der code ist von mir.

    var Emitter = function () {
	var emitter = this instanceof Emitter === false ? new Emitter() : this;
	if (typeof emitter._eventListeners === "undefined") { // ^
	    Object.defineProperty(emitter, "_eventListeners", {
		value: {},
		enumerable: false
	    });
	}
	return emitter;
    };
    Emitter.prototype = {
	constructor: Emitter,
	on: function (event, callback) {
	    if (!this._eventListeners[event]) {
		this._eventListeners[event] = [];
	    }
	    this._eventListeners[event].push(callback);
	    return this;
	},
	once: function (event, callback) {
	    var emitter = this;
	    var removable = function () {
		callback.apply(this, arguments);
		emitter.remove(event, removable);
	    };
	    this.on(event, removable);
	    return this;
	},	
	emit: function (event) {
	    var i, j, list, args, emitter = this;
	    if (list=this._eventListeners[event]) {
		j = list.length;
		if (j > 0) {
		    args = [].slice.call(arguments,1);
		    for (var i=0; i < j; i++) {
			list[i].apply(emitter, args);
		    }
		}
	    }
	    return this;
	},
	remove: function (event, callback) {
	    if (this._eventListeners[event]) {
		this._eventListeners[event] = 
		this._eventListeners[event].filter(function (listener) {
		    return listener !== callback;
		});
	    }
	    return this;
	},
	removeAll: function (event) {
	    if (this._eventListeners[event]) {
		this._eventListeners[event] = [];
	    }
	    return this;	
	}
    };

Prototypal Inheritance Goodie (Achtung, Bug!)

Dem Prototype eines sollte man keine leeren Objekte oder Arrays zuweisen, die zur Laufzeit verwendet werden. Der eignet sich nur fuer Konstanten und Methoden, oder fuer einen "Shared Dispatcher", denn alle Emitter Instanzen, die zum prototype pushen, pushen in das selbe Objekt. Traurig, aber wahr. Darum erzeugt man in JavaScript lieber this._eventListeners = {} im Konstruktor, anstatt mit Emitter.prototype._eventListeners = {} einen gesharetes Objekt zu erzeugen.

Angenommen, ich habe 10 Module, als Event Emitter, und habe das _Listener Objekt im Prototype verstaut, dann habe ich alle 10 Listener spaeter in einem Objekt, anstatt 10 Emitter mit je 1 Listener. Darum sollten nur Funktionen dort stehen. Und auch dort sollte man bereits wissen, dass (function () { return function() {} }()); zu einem aehnlichen Effekt fuehrt. Einen Counter darin wuerden sich auch alle teilen.

Um den Emitter auszuprobieren:


    var e = new Emitter();
    e.on("test", function (yeah, it, works) {
	console.log(yeah + " " + it + " " +works);
    });    
    e.emit("test", "no", "it", "doesn't");
    // no it doesn't
    e.emit("test", "yes", "it", "does");
    // yes it does

Das kann ich noch alternativ vom DOM bieten, ist ein Tip fuer euer Framework und die Objektkommunikation. Der ist einfacher als das grosse Dom Ding (wenn das auch nur ein paar Vars sind).

Achgott, ich rede Unsinn, was heisst gross. EventTarget ist klein, wie Event. Das solltet ihr unbedingt (!) unbedingt (!) selbst ausprobieren. Das kann man sich merken.

MutationObserver

Das andere Standbein im DOM4 ist der Mutation Observer

Der überwacht die Veraenderung mit einem Log von MutationRecords. Der löst die Mutation Events (DOMNodeInserted und Co.) ab, weil er nicht so oft feuert. Das ist besser für die Stabilität des Browsers, weil die alten Events die ganze Zeit nur gefeuert haben und eher zum Crashen neigten.

Mehr dazu das naechste mal.

Das ist weitverbreitet...und darf hier nochmal genau unter die Lupe genommen werden. Beim naechsten mal.

Publisher-Subscriber

Ein anderes Modell, Nachrichten an zu übermitteln.

Das ist nicht genau das richtige Observer-Notifier/Publisher-Subscriber, sondern eine Variante von mir, rauskopiert aus meinem letzten Loader Source.

    Publisher = function () {
        var publisher = this instanceof Publisher === false ? new Publisher() : this;
        Object.defineProperty(publisher, "_subscriptions", {
            value: {},
            enumerable: false
        });
        return publisher;
    };
    Publisher.prototype = {
        constructor: Publisher,
        subscribe: function (event, callback) {
            if (typeof event === "string" && typeof callback === "function") {
                this._subscriptions[event] = this._subscriptions[event] || [];
                this._subscriptions[event].push(callback);
            } else {
                throw new TypeError("Wrong arguments: event (string), callback (function) required");
            }
            return this;
        },
        publish: function (event) {
            var publisher = this;
            var subscribers = this._subscriptions[event];
            var args = [].slice.call(arguments, 1);
            if (subscribers) {
                forEach(subscribers, function (s) {
                    if (typeof s === "function") {
                        s.apply(publisher, args);
                    }
                });
            }
            return this;
        }
        /* unsubscribe: function () */
    };

Als ich herausfinden wollte, wo ich ich es herhabe, fand ich es hier wieder Addy Osmani: Large Scale JavaScript

Mediator

Zentraler Vermitter für Nachrichten

Ich habe das Mediator Pattern, was Addy Osmani beschreibt mal abgeschrieben und dabei ein wenig verändert. Dieses Objekt ist jetzt ausserdem manipulationssicher. Es könnte sonst andere Funktionen zuweisen. Was es macht? Es weisst Publish und Subscribe zu, die es ausserdem selbst hat. Um die Messages kümmern sich die Subscriber auf einem Channel.

Wo das angewandt wird, weiss ich nicht. Wie man sieht ist der Mediator ein zentraler Vermittler für Nachrichten, wo man viele Objekte hat und nicht gerade Alice mit Bob verlinkt.

	
    // Das ist jetzt keine 1:1 Kopie, sondern von mir paraphrasiert,
    // seht selbst, es steht in addyosmani.com/largescalejavascript/
    // und in /resources/essentialsjsdesignpatterns/book/

    // Was ich direkt probiebe, ist es mit ES5 manipulationssicher
    // ob es sofort funktioniert, wir werden sehen, es sollte, ich werde
    // es nicht pruefen, bevor ich diese Seite aktualisiere 

    function Mediator () {
	"use strict"; 
	var channels = {};
	var subscribe = function (ch, f) {
	    if (!Array.isArray(channels[ch])) channels[ch] = [];
	    channels[ch].push({ context: this, callback: f });
	    return this;
	};
	var publish = function (ch) {
	    if (!Array.isArray(channel[ch])) 
		// throw new TypeError("Invalid channel");
		return false;
	    var args = [].slice.call(arguments, 1);
	    channels[ch].forEach(function (subscriber) {
		subscriber.callback.apply(subscriber.context, args);
	    });
	};
	return Object.freeze({	// mediator Object (frozen)
	    publish: publish,
	    subscribe: subscribe
	    installTo: function (obj) {
		obj.subscribe = subscribe;
		obj.publish = publish;
	    }
	});
    }
    // Mal kurz ausprobieren

    var mediator = Mediator(); // new hat keinen Effekt (keine richtige Contructor Funktion)
    var obj1 = {};
    var obj2 = {};
    mediator.installTo(obj1);
    mediator.installTo(obj2);
    obj1.subscribe("test", function (str) {
	console.log(str);
    });
    obj2.subscribe("test", function (str) {
	console.log(str);
    });
    mediator.publish("test", "Zusicherung: Jedes Objekt loggt einmal diesen String");

Achtung, dieses Kapitel ist wichtiger als die Examples ausdrücken können, und ich habe meinen Code noch verbessern können. Mehr dazu demnächst, dann ist dieses Kapitel auf einmal etwas anders. Hier kann man noch dazulernen, obwohl es schon angedeutet ist. Und hier kann man den Code verbessern. Object.create und defineProperty kann man professioneller verwenden. Ausserdem steckt in diesem Kapitel der Schluessel zur neuen ES5 Sicherheit, die endlich moeglich ist. Vor ES6 eine clevere Studie wert.

Object Meta API

Accessors

Getter und Setter

Man unterscheidet zwischen Accessor: get und set und Data Property: value

, wenn man Object.defineProperty benuttzt.

Wenn man den Object prototype öffnet stösst man auf mehrere Operationen __defineGetter__, __defineSetter__, __lookupGetter___, __lookupSetter___. Mit diesen lässt sich eine -ich nenne sie mal- "fake-Variable" einrichten, deren Lesefunktion (get) a = object.fakeVar; und Schreibfunktion (set) object.fakeVar = "irgendwas" selbst bestimmt werden kann.


    // Achtung, diese __ Funktion sind zwar schon in ES3 Browsern vorhanden.
    // Aber: Sie sind kein Standard 

function O() {
    this.welt = "Welt";
    this.__defineGetter__("hallo", function() {
	return this.welt;
    });
    this.__defineSetter__("hallo", function(value) {
	this.welt = value;
    });
}
O.prototype = {
    constructor: O,
    get random() {
	return Math.random();
    },
    set fakeVar(v) {
	this._saveVar = v;
    },
    get fakeVar() {
        return this._saveVar;
    } 
};
    
var o = new O();
console.log( o.hallo ); 	// Welt
console.log( o.hallo = "Edward" ); // Edward
console.log( o.hallo );		// Edward
console.log( o.random );	// 0.02286206791177392 oder eine andere Zahl
o.fakeVar = 2000;
console.log( o.fakeVar );	// 2000

Wenn man nur einen Getter schreibt, hat man eine Read-Only Variable. Wenn man nur einen Setter schreibt, hat man einen Write-Only Zugriff.

Das DOM ist voll mit Definitionen von readonly Variablen, die vom System initialisiert und so ausgegeben werden. Der Browser rechnet aus und wir erhalten das Resultat ueber eine Variable, die durch einen Getter repräsentiert wird.

Getter und Setter mit dem Objekt "Object" in ECMAScript 5

Hier kommen nun Object.defineProperty und Object.defineProperties zum Einsatz.


function O() {

    // Eine Property definieren.
    Object.defineProperty(this, "hallo", {
	get: function() {
	    return this.welt;
	},
	set: function(v) {
	    this.welt = v;
	},
	enumerable: true,
	writeable: true,
	configurable: true
    });

    // Oder als Plural. Objekt mit Descriptor Objekten.
    
    Object.defineProperties(this, {
	"xyz": {
	    value: "abc",
	    writeable: false
	},
	"mno": {
	    value: "pqrs"
	}
    });

}


// besser ist Object.create(new function(){}, { descriptormap });

var o = new O();
console.dir(o);
    // O
    // get hallo: function
    // mno: "pqrs"
    // set hallo: function
    // xyz: "abc"
    // __proto__: O

get und set und die booleans können nicht alle miteinander kombiniert werden. Zum Beispiel kann man set nicht mit writeable: false vereinbaren

    get, set sind accessor properties
    value, writeable, enumerable, configurable sind variable properties

Neben der Moeglichkeit die Getter und Setter zu definieren, kann man auch noch festlegen, ob die Property aufzälbar (enumerable) ist. Sie erscheint dann nicht mehr bei Object.keys(object) oder for (var prop in object)

    var MyArray = function () {
	var args = arguments, i = 0, j = args.length;
	for (;i<j;i++) {
	    this[i] = args[i]
	}
	Object.defineProperty(this, "length", {
	    value: i,
	    enumerable: false
	});
	Object.defineProperty(this, "push", {
	    value: function (item) {
	        this[this.length] = item;
		this.length += 1;
	    },
	    writeable: false,
	    configurable: false
	});
    };
    MyArray.prototype = [];

Mit Object.getOwnPropertyNames(object) bekommt man nicht nur alle Properties geliefert, sondern auch die, wo enumerable false ist.

..
    // In einer nativen Shell (fast) die einzige Moeglichkeit das globale Objekt
    // zu listen, bis man eine API programmiert.
    
    Starting in C++ Shell Mode...
    Spiderman> Object.getOwnPropertyNames(this);
    undefined,Function,Object,eval,Array,Boolean,Error,InternalError,EvalError,
    RangeError,ReferenceError,SyntaxError,TypeError,URIError,Math,isNaN,isFinite,
    parseFloat,parseInt,Number,NaN,Infinity,JSON,RegExp,escape,unescape,uneval,
    decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,String,Int8Array,
    Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,
    Float64Array,Uint8ClampedArray,ArrayBuffer,Namespace,QName,isXMLName,XML,
    XMLList,Iterator,StopIteration,Date,Proxy
    Spiderman> 

    // Das ist der einzige Blick rein ins globale Objekt zu Beginn der neuen Shell.

writeable bedeutet, ob man die Variable setzen kann (geht nicht in Kombi mit setter).

    // readonly mit accessor/getter
    var obj = {
	get readOnly () {
	    return 42;
	}
    };
    
    // readonly mit writeable:false
    Object.defineProperty(obj, "nurLesen", {
	value: 42,
	writeable: false
    });
    
    obj.readOnly;
    // 42;
    obj.nurLesen;
    // 42;

    // Versuchen wir zu setzen    
    obj.readOnly = 22;
    obj.nurLesen = 22;

    // Geht nicht...sind nur lesbar
    obj.readOnly;
    // 42;
    obj.nurLesen;
    // 42;

configurable legt ausserdem noch fest, ob man die Variable kopieren kann.

value legt ausserdem noch den Wert fest, mit dem die Eigenschaft initialisiert wird.

    function X() { }
    function Y() { }
    X.prototype = new Y();

    Object.defineProperty(X.prototype, "toString", {
	value: function () {
	    return "[object XY]";
	},
	enumerable: true,
	writeable: false,
	configurable: false
    });
    
    var x = new X();
    x.toString();
    // [object XY]

Mit Object.getOwnPropertyDescriptor(object, name) kann man diese Konfiguration auslesen und man erhaelt das Descriptorobjekt für name.

    var obj = {
	foo: "bar",
	get baz () {
	    return "daz";
	}
    }
    var pdm_foo = Object.getOwnPropertyDescript(obj, "foo");
    var pdm_baz = Object.getOwnPropertyDescript(obj, "baz");
    

o.isPrototypeOf(test) Schaut nach, ob o prototype von test wäre.

    
    var x = { y: 1, z: 2 };
    var obj = Object.create(x);

    x.isPrototypeOf(obj);
    // true

    var proto_x = Object.getPrototypeOf(obj);
    proto_x === x;
    // true

o.propertyIsEnumerable("x") erzählt, ob o.x aufzäbar ist.

    var a = [ 1,2,3,4,5 ];
    
    // Gucke, ob die length Property eines Arrays enumerable ist (nein);
    console.log(Object.propertyIsEnumerable.call(a, a.length);

Object.preventExtensions(object), Object.isExtensible(object): Wenn die Funktion gerufen wird, kann man keine neuen Member mehr zum Objekt hinzufügen. Kann nicht mehr rückgängig gemacht werden.

    var object = { a: 1, b: 2 };
    var unextensible = Object.preventExtensions(object);
    unextensible.c = 3;
    console.dir(unextensible);
    
    if (Object.isExtensible(object)) {
	console.log("object kann man properties added");
    }
    
    if (!Object.isExtensible(unextensible)) {
	console.log("object kann man properties added");
    }
    
    unextensible.b = 3;
    if (unextensible.b === 3) {
	console.log("vorhandene properties kann man noch aendern");
    }

Object.seal(object), Object.isSealed(object): Wie .preventExtensions, setzt aber zus&aum;tzlich noch "writeable" auf false;

    var object = { a: 1, b: 2 };
    
    var sealed = Object.seal(object);
    
    sealed.b = 3;
    if (sealed.b !== 3) {
	console.log("konnte sealed.b nicht setzen");
    }
    if (Object.isSealed(sealed)) {
	console.log("sealed ist versiegelt");
    }

Object.freeze(object), Object.isFrozen(object): Ebenso, setzt aber noch "configurable" auf false.

    var object = { a: 1, b: 2 };
    
    var makeFrozen = function (object) {
	var F = function () {};
	F.prototype = object;
	return Object.freeze(new F());
    };

    var frozen = makeFrozen(object);

Hier kann man sichergehen, tamper-proof, d.h. manipulationssichere Objekte zu bekommen.

Ok, bessere Informationen gibt es bei Mark S. Miller und Changes to ECMAScript auf GoogleDevelopers bei YouTube. Der erste Teil beschreibt unter anderem diese Objekt API und der zweite beschreibt sie komplett und nutzt sie voellig aus, ausserdem werden Proxies vorgestellt (in Firefox und Chrome von heute vorhanden, Leute). Auf http://code.google.com/p/es-lab/ gibt es die Slides und den Code.

Ok, machen wir weiter. Ich implementiere eine Art DEEP_FREEZE, wo ich saemtliche Properties, die ebenfalls Objekte oder Funktionen sind traversiere und einfriere. Variablen lasse ich diesmal offen. Mein Beispiel soll nur die Traversion für ein tieferes Einfrieren zeigen.


    function deepFreeze(object) {
	if (typeof object === "object" || typeof object === "function" && typeof object !== "null") {
	    for (var p in object) {
		if (Object.hasOwnProperty.call(object, p)) {
		    if (typeof object[p] === "object") { // falls property ein Object ist.
			// objekte traversieren
			deepFreeze(object[p]); // gehe rekursiv das von Objekt zu Objekt
		    } else if (typeof object[p] === "function") {
			// functions
			object[p] = Object.freeze(object[p]);
		    } else {
			// alle anderen properties
			object[p] = Object.seal(object[p]); // writeable:true
		    }
		}
	    }
	} else return Object.seal(object); // numbers, bools, strings (writeable is an)
    }

Wenn man hasOwnProperty jetzt weglaesst, versucht man den Prototype mit einzufrieren. Das einfach irgendwo anzuwenden macht natuerlich keinen Sinn, und das ist nur für Sachen gemacht, die ihr nicht mehr aendern wollt. Wenn ihr ein Objekt ganz einfriert, macht es Sinn, Variablen in Closures zu halten. Die koennen nicht geknackt (aber debuggt) werden und sind vom Freeze nicht betroffen.

Anstelle von Object.freeze kann man auch die Descriptormap auslesen und neu setzen. Moeglich ist das aber nur mit ES5 mit ES3 gibt es keine richtige Change, was tamper-proof zu machen.

Ok, auch der Irrtum mit dem Observerpattern Name hat sich geklaert. Das alte Buch von Gamma hat sie alle. Das kriege ich schon wieder geordnet.

Needs Update (rewrite)

Observer-Notifier mit ES5

(Variationen gibt es davon unzaehlige)

Wichtig ist es auch mitunter, Properties zu ueberwachen. Wie man bei Accessors und Object lesen kann, kann man mit den ES5 Funktionen nicht nur Funktionen wie innerHTML schreiben var emul = { get innerHTML () { return "<h1>Habe eine Idee</h1>"; } }; console.log(emul.innerHTML);.

Mir ist bereits aufgefallen, dass es Viewbinding gibt, und auch vor einigen Wochen habe ich schonmal eine Databinding Funktion für DOM geschrieben, zumindest kann ich mit den Accessorn Objekte mit Callbacks ausstatten, die mir die entsprechend registrierten View Operationen ausfuehren.

Dann legen die Mutationen im DOM z.B. ein Log an. Das habe ich noch nicht abgeschrieben, oldValue ist das, was ich behalten habe, hehe.


// Ergebnis:

{ setter: 'x', oldValue: 1, newValue: 1 }
setter: y
{ '0': 2 }
{ setter: 'y', oldValue: 2, newValue: 2 }
getter: x
{}
{ getter: 'x', value: 1 }
getter: y
{}
{ getter: 'y', value: 2 }

Kommen wir zu einem statischen Stellvertreter fuer ein Objekt. Man weist dem proxy Daten zu, er gibt sie an das richtige Objekt weiter, notifiziert aber vorher seinen Observer.

static proxy mit Object.defineProperty

Das ist jetzt nicht optimiert, aber definitiv eine Garantie, dass man das so machen kann. Und natuerlich im DOM mit Event Listener bei Change Events und Blur Events oder insbesondere mit dem MutationObserver. Die Mutation Events sind bereits deprecated. Die MutationObserver hingegen nicht. Das ist der Ersatz dafuer.

Hier ist mal eine einfache Observe Funktion

var module = { exports: {}, parent: null }
var exports = module.exports; // browser fix fuer copy und paste

// Source:
exports.observe = (function () {	// Ich denke man, dass es gleichzeigt ein "static proxy" ist.
    return function (object, callback) {
	var proxy = {};

	Object.keys(object).forEach(function(key) {
	    Object.defineProperty(proxy, key, {
		get: function () {
		    console.log("getter: "+key);
		    console.dir(arguments);
		    callback({ getter: key, value: object[key] });
		    return object[key];
		},
		set: function (v) {
		    console.log("setter: "+key);
		    console.dir(arguments);
		    var oldValue = object[key];
		    object[key] = v;
		    callback({ setter: key, oldValue: oldValue, newValue: v });		    
		}
	    });
	});
	return proxy;
    };
}());

// Ausprobieren mit node observe.js (oder editieren und auf den Browser kopieren)
// Ich mache ein JavaScript.html Kapitel raus, ist sicher.

if (!module.parent) {
(function () {
    
    var o = { x: 1, y: 2, z:3 }; // Das Objekt muss bereits keys haben
				// und kann neue nur ueberwachen, wenn
				// man den observer neu anmeldet.
    var o2 = {}
    var observer = function (record) {
	console.dir(record);
    };
    o = exports.observe(o, observer);
    o.x = 1;
    o.y = 2;
    o2.x = 1;
    o2.y = 2;
    o.x;
    o.y;

}());

}

Hier mein missratenes Changelog.


// Ergebnis:

{ setter: 'x', oldValue: 1, newValue: 1 }
setter: y
{ '0': 2 }
{ setter: 'y', oldValue: 2, newValue: 2 }
getter: x
{}
{ getter: 'x', value: 1 }
getter: y
{}
{ getter: 'y', value: 2 }

Harmony Proxies (ES6 finally)

Kurz angetestet, wie einfach es waere damit Property-Observer zu schreiben.

#!/usr/local/bin/node --harmony_proxies  

exports.observeObject = function (object) {
     var handler = {
        get: function (obj, name) {
    	    console.log("get: "+ name);
    	    return object[name];
        
        },
        set: function (proxy, name, value) {
            console.log("set: " + name + " = " +value );
            return object[name] = value;
        },
        has: function (proxy, name) {
    	    return Object.hasOwnProperty.call(object, name);
        },
        delete: function (name) {
            console.log("delete: " + name);
    	    return delete object[name];
        },
        enumerate: function () {
    	    return ["first"];
        }
     };
     var proxy = Proxy.create(handler, object);
     return proxy;
};

if (!module.parent) {
    var test1 = {
	first: "Edward",
	last: "Gerhold"
    };
    
    var p1 = exports.observeObject(test1);
    console.log(p1.first);
    p1.first = "Eddi";
    p1.last = "Gerhi";
    for (var p in p1) {
	console.log("enumerate: "+ p);
    }
    console.log(test1.first);
    console.log(test1.last);
    p1.x = 12;
    console.dir(test1.x);
    delete p1.x;
    
}

Ausgabe

get: first
Edward
set: first = Eddi
set: last = Gerhi
enumerate: first
Eddi
Gerhi
set: x = 12
12
delete: x

Damit sind in MVCs vollautomatische Propertyobserver von _unpraeparierten_ Ojekten fürs Viewbinding finally moeglich. Obwohl die Callbackvariante voellig reicht und cool ist. Ist das hier natuerlich mehr wert. Und der Gedanke an das typische moderne Webapp Framework ist schon richtig. Aber mit den Proxies ist noch viel mehr drin.

hasOwnProperty

Nachgucken, ob eine Property vorhanden ist.

    var o = {
	"a": "abc",
	"b": "bcd"
    };
    

für EcmaScript next offiziell im Gespräch own.

    // own keyword 
    for own (key in obj) {
	// hasOwnProps only
    }
    

Was man in ES5 und 3 macht ist folgendes:

    for (var p in o) { // geht alle Properties inklusive Prototype durch
	if (o.hasOwnProperty(p)) { // filtert die eigenen Properties (ohne Prototype)
	    console.log("Yes");
	}
    }
    
x
    // Falls ein Object kein hasOwnProperty hat siehe z.B. Object.create(null) Objekte:
    if (Object.hasOwnProperty.call(o, "a")) {
	console.log("Yes");
    }
    
x
    // Man kann auch "if (prop in object)" schreiben!! Der in-Operator geht auch im if.
    if ("a" in o) {
	console.log("Yes");
    }
    

ECMAScript 5 bietet Object.keys(o).forEach/map/filter/reduce..

	var o = {
	    "a": "abc",
	    "b": "def",
	    "c": "ghi"
	};
	
	Object.keys(o).forEach(function(key) {
	    console.log("key=" + key + ", value="+ o[key]);
	});
    
    

Anzahl der Properties rausfinden, und zwar leichter, als muehselig selbst die Properties zu filtern geht es in ES5:

	Object.keys(o).length;
	// 3 (a, b, c)
    

Wie man Object.keys implementieren kann, wenn man ES3 kompatibel werden will.

    if (typeof Object.keys !== "function") {
	Object.keys = function (object) {
	    var result = [];
	    for (var name in object) {
		if (Object.hasOwnProperty.call(object, name))
		    result.push(name);
	    }
	    return result;
	};
    }
    

arguments

arguments ist besondere, nüzliche Variable, die alle Argumente einer Funktion in der Reihenfolge, in der sie uebergeben wurden, speichert. Eine Funktion die nur drei Parameter hat, aber fuenf uebergeben kriegt, findet die anderen zwei dann übrigens im arguments Array.

    function x(a,b,c) {
	console.log( a === arguments[0] ); // true
	console.log( b === arguments[1] ); // true
	console.log( c === arguments[2] ); // true
	console.log( arguments.length ); // 3
    }
    
    console.log( x.name ) // "x"
    console.log( x.length ) // 3 (Funktionsname.length === Anzahl der Attribute)
    console.log( x.arguments ) // null (noch)

In ES3 war arguments kein richtiger Array und man musste Array.prototype.*.call(arguments, parms) darauf anwenden. In ES5 ist arguments ein richtiger Array? Falsch. arguments hat eine splice Methode

    var printargs = function () {
	var args = arguments.splice(0);
	args.forEach(function(a) {
	    console.log(a);
	});
    };

Hatte man in ES3 Arguments veraendert, veraenderten sich a, b, und c ebenfalls. In ES5 ist auch das Problem behoben und a, b, c bleiben unveraendert.

    // Das Beispiel ist harmlos. Aber es koennen einem die Parameter "verrutschen" oder "kaputtgehen" wenn man nicht weiss, was man macht.
    
    var f = function (a, b, c) {
	arguments[0] = 7;
	console.log(a);
	console.log(b);
	console.log(c);
    };
    f(1,2,3);
    // 7,2,3

Wie man am Beispiel sieht kann man benannte Funktionen um ihren Namen bitten, der name bei anonymen ist "", oder auch um .length - die Anzahl der Argumente. Oder um arguments selbst, was ausserhalb der Invokation noch null ist.

arguments in der Praxis

Das hier ist ein Beispiel, wie vielseitig Funktionen werden kann, wenn man ihre Parameter ueberprueft. Anders als in fest-typisierten Sprachen, wo man überladen kann, kann man mit dem Auswerten der arguments ebenso geniale Ergebnisse erzielen.

    // return: instanceof Library
    var lib = new Library();

    // return: LIB, ruft aber AMD require
    var lib = Library([deps], factory); // require Modules/Asynchronous
    
    // return: lib, ruft aber lib.use(names..., function (lib, require))
    var lib = Library("a", "b", "c", function (lib, require) [, options]);
    var lib = Library({ use: [], ... }, function (lib, require)); // options als arguments[0]
    var lib = Library({ require: [], }, function (a, b, ...)); // Als AMD Loader 
        
    // Und OHNE var lib = 
    // return: PROMISES
    var promise = Library.requireAsync(deps, factory);
    var promise = Library.ensure(deps, function (require));

    // return: EXPORTS cache oder sync
    var exports = Library.requireAsync(string);
    var exports = Library.requireSync(string);
    var exports = Library(string) //  === require(string);

Kein Standard

arguments.caller oder arguments.callee. In Ecma Script 5 strict mode null.

Abhängigkeiten laden

Externe JavaScript Files, CommonJS Module, AMD

Es gibt verschiedene Möglichkeiten, Skripte zu laden.

Browser

1. Laden mit dem Script Tag im HTML Source

<script src="addon.js"></script>

2. Laden mit dem Script Tag via JavaScript

<script>
    var script = document.createElement("script");
    document.body.appendChild(script);
    script.onload = callback;
    script.onerror = callback;
    script.async = true; // asynchron
    script.src = "addon.js"
    // oder wenn man kein File, sondern Daten hat:
    // script.textContent = sourceCode;
</script>    

Achtung: Wenn man einen script-Tag mit anderem HTML einer innerHTML Variable eines Elements zuweist, wird das Skript nicht automatisch ausgeführt. Man muss es dann nochmal extrahieren, ein neues Skript Element erzeugen, den textContent zuweisen und die neue Node ins DOM einhängen. Dann funktioniert das Skript.

3. Laden mit einem Function Objekt

    // mysource.js:  	
    console.log( x );
    console.log( y );
    console.log( z );
    return x * y * z;

    // Laden mit Funktionsobjekt (lib.io.get is eine Abkuerzung fuer XMLHttpRequest)
    lib.io.get("mysource.js", function(response) {
	var code = new Function("x","y", "z", response);
	console.log( code(1,2,3) );
    });
    
    // Resultat:
    // 1
    // 2
    // 3
    // 6

Der Code in mysource.js bekommt sozusagen ein function(x,y,z) { /* mysource.js*/ } durch das Funktionsobjekt um sich gelegt. Da man das wie eine normale Funktion mit .call, .apply und .bind rufen kann, hat man enorme kontrolle gegenueber Script Tag loading.

Worker

1. Laden mit importScripts(name)

// worker.js
importScripts("worker-addon.js");

Worker haben kein Window und kein Document (kein DOM, sigh). Skripte die das brauchen können nicht geladen werden.

Server

1. Laden mit der CommonJS require Funktion. Diese Funktion wird synchron ausgeführt.

    var addOn = require("./addon").addOn;

2. Code eines CommonJS Modules. Alles was man requiren kann, wurde im Modul selbst der globalen Variable exports zugewiesen.

    var addOn = {
	text: "blah"
    };
    
    exports.addOn = addOn;

Es gibt auch die Variable module, die das Modul identifizieren kann.

Wie ich an exports rankomme.
    // commonjs.js
    exports.test = "abc";

    // Dieser XHR ist synchron, darum keine onload Funktion
    // Der xhr.responseText kann direkt gelesen werden
    // Copy + Paste auf die Console (F12) zum ausprobieren

    var require = function(string) {
	var module = { exports: {} }, exports = module.exports, f;
	var xhr = new XMLHttpRequest();
	xhr.open("GET", string, false); // sync
	xhr.send(null);
	f = new Function("exports", "module", "require", xhr.responseText);
	f(module.exports, module, require);
	module.id = string; // Die darf das Modul nicht ueberschreiben
	return module.exports;
    };

    var exports = require("commonjs.js");
    console.dir(exports);
    
    // > XHR finished loading: "http://linux-swt.de/commonjs.js"
    // > module.id === commonjs.js
    // > Object /* exports.test === "abc" */

AMD

Achtung. Ich ueberhole gerade mein AMD und CommonJS Wissen, da ich es die Tage im Juli selbst implementiere.

	// object.js
	define(function() {
	    return {};
	});
	
	// impl.js
	require(['object'], function(o) {
	    console.log (o === {});
	});

Oben ist der Filename gleich dem Modulnamen, weil keiner bei define Angegeben ist

Mit requireJS habe ich noch nicht gearbeitet, ich werde aber die CommonJS / AMD Spec für meine Library nun einbauen, dann vervollstaendige ich das Kapitel.

Dann gibt es verschiedene Variationen, Beispiel Modules/Async/A

    
    require.ensure(["./script"], function (require) {
	var script = require("./script");
	//...
    });
    

    // hier wird der Cache vorher populiert, dann funktioniert der Code
    // mit require(string)    
    

Harmony Modules (ES6)

	module TestModul {
	    export function test1 () { console.log("test1"); }
	    export function test2 () { console.log("test2"); }
	}
	
	import {test1, test2} from TestModul; 
	// {test1,test2} ist es6 notation fuer {test1:test1,test2:test2} 
	
	import $ from "http://jquery";
	
	// module TestModul from "testmodul.js"
    

Den Rest muss ich noch rausfinden...

Abstürze vermeiden

    // Testen, ob die Eigenschaft existiert.

    if (object.property) {
	// ...
    }

Wenn property nicht definiert ist, passiert jetzt nichts, sollte object aber auch nicht definiert sein, stürzt das Skript ab. An solchen Stellen sollte man nicht nur die Property, sondern auch das Objekt selbst zuerst prüfen.

    // ERROR: object is not defined
    
    if (object && object.property) {
	    ....
    }

Wenn man nicht weiss, ob die Variable ueberhaupt existiert, ist nur typeof sicher.

    // Ist fuer node, laueft aber im Browser ohne Absturz
    // Der Ausdruck wird richtig nach false evaluiert
    if (typeof process !== "undefined") {
	process.argv.forEach(function (arg) { console.log(arg); });
    }

Operatoren

&, |

Bitweises UND und ODER wird zum Abgleich von Bitmasken verwendet.

	// Anstatt mit (n % 2 16=== 1) kann man mit (n & 1) auch ungerade Zahlen abtesten
	
	> 1 & 1
	1
	> 2 & 1
	0
	> 3 & 1
	1
	> 4 & 1
	0
	> 5 & 1
	1
	

Versuchen wir es mal mit anderen Zahlen.

	    > 32768 & 64
	    64
	    > 32767 & 64
	    64
	    > 64 & 64
	    64
	    > (64 + 32767) & 64
	    0
	    > (64 + 32768) & 64
	    64
	    > (64*64 + 32767) & 64
	    64
	    > (64*64 + 32768) & 64
	    0
	    > 28 & 8
	    8
	    > 20 & 8
	    0
	

Mir geht gerade die Erklaerung aus. Tut mir leid, aber so kann man die Masken lesen:

	var body = document.querySelector("body");
	var child = body.firstChild;
	var div = document.createElement("div"); // nicht appenden
	
	cdp(body,child);
	cdp(child, body);
	cdp(div, body);
	cdp(body, div);
	
	function cdp (a, b) {
	    console.log("cdp: ");
	        
	    var bitmask = a.compareDocumentPosition(b);
	    
	    if (bitmask & Node.DOCUMENT_POSITION_CONTAINS) {
		console.log("docpos_contains");
	    }
	    if (bitmask & Node.DOCUMENT_POSITION_CONTAINED_BY) {
		console.log("docpos_contained_by");
	    }
	    if (bitmask & Node.DOCUMENT_POSITION_PRECEDING) {
		console.log("docpos_preceding");
	    }
	    if (bitmask & Node.DOCUMENT_POSITION_FOLLOWING) {
		console.log("docpos_following");
	    }
	    if (bitmask & Node.DOCUMENT_POSITION_DISCONNECTED) {
		console.log("docpos_disconnected");
	    }	    
	    
	}
	
	// cdp:
	// docpos_contained_by
	// docpos_following
	// cdp:
	// docpos_contains
	// docpos_preceding
	// cdp:
	// docpos_disconnected
	// cdp:
	// docpos_disconnected
	
	
	
	
===, !==

Strict Equal wandelt den Typ nicht um, sondern erwartet den gleichen Typ.

==, !=

Equal versucht erst in den gleichen Typen zu verwandeln, dann zu vergleichen.

Bitweises << shiften

Achtung: In JavaScript ist ein SHIFT langsamer als eine normale Multiplikation oder Division, weil die Zahlen erst konvertiert werden. In JavaScript taeuscht es, << oder >> zu nutzen, um schnell zum Ergebniss zu kommen. Der Rechner braucht in Wahrheit laenger, da er zwischen Integer und Float hin- und herkonvertiert.

Multiplikation mit 2 mit 2 mit 2 (a << 3)

a * 2^n

> 16 << 1 (16 *2)
32	
> 16 << 2
64
> 16 << 3
128
> 16 << 4
256
> 16 << 5
512
Und nach rechts >> shiften

Division durch 2 durch 2 durch 2

a / (2^n)

> 8 >> 1 (8/2)
4
> 8 >> 2 (8/2/2)
2
> 8 >> 3 (8/2/2/2)
1
> 8 >> 4
0
> 8 >> 5
0

Oder jede andere Zahl *2 oder /2

> 13 << 4 (13 * 2 * 2 * 2 * 2)
208
13 << 1 (13 * 2)
26

Hier kommt aber 6 und nicht 6.5 raus.

> 13 >> 1	// 13 / 2^1 != 6.5 ???
6
> 13 >> 2
3

Auch wenn ich mir gerade eine Frage stellte, es ist so und wird so bleiben. Es wird gerundet oder truncated.

> 4 >> 1
2
> 8 >> 1
4

>>> Vorzeichenloses Right Shift

Den habe ich auch noch nicht benutzt.

>1 >>> 4
0
> 8 >>> 1
4
> 8 >>> 2
2

Ich habe gesehen, wie man ihn gegen eine .length Property einsetzt. Ich kann mir denken warum. Heraus kommt eine frische Number, anstatt eine Referenz auf die .length Property.

    // Das erzeugt eine Referenz auf .length
    // Das ist nicht immer das, was man will, wenn sich
    // der Array veraendert, veraendert sich just_ref mit
    // vielleicht will man aber nur die Zahl
    // var just_ref  = [1,2,3,4,5,6,7,8,9,0].length;

    // So kriegt man nur die Zahl
    // mit ">>> 0" wird eine neue Nummer kreiert:

    var newnumber = [1,2,3,4,5,6,7,8,9,0].length >>> 0;
    console.log(newnumber); // 10

    // [].length >>> 0 entspricht wohl "new Number"
    // var otherwise = new Number([1,2,3,4,5,6,7,8,9,0].length);

    // oder mit "+" geprefixed ergibt auch eine Number
    // var anotherone = +[1,2,3,4,5,6,7,8,9,0].length;

Links && Rechts (Logisches UND)

Wenn Links wahr ist, ist das Resultat Rechts.

Der Guard Operator

    

Lässt vermuten, dass man sich ein if-statement kürzen kann. Und eine Zuweisung ausführen kann, nur _wenn_ object wahr ist.

object && object.property = "a"; // - geht in Chrome // - gibt eine Meldung im Firefox, invalid left-hand in assignment // Der Firefox denkt (object && object.property) = "a"; // object && (object.property = "a"); waere ein entgegenkommen. // - keine Ahnung was bei den andere ist.
    

// Darum lieber richtig schreiben:

if (object) { object.property = "a"; } // - gibt natuerlich dann keine Meldung im Firefox, weil // es ein normales if Statement (Anweisung) ist.

Voellig normal ist hingegen...

    callback && callback.call(this, event);

(Eine Invokation einer Funktion)

Links || Rechts (ODER)

Wenn Links wahr, ist das Resultat Links.

Der Default Operator

	if (nicht_dies || dann_das) {
	    // if (!nicht_dies) { if (dann_das) { .. } }
	}

=== und !==

== und !=

Verwendet einfach das dreifache Gleichzeichen ===. Es ist das genau von beiden Möglichkeiten, == oder === zu nutzen.

    true == 1  // true
    true === 1  // false
    
    false == 0  // true
    false === 0 // false

Das doppelte Gleichzeichen == führt die "type coercion" durch, das ist ein automatisches Casting, eine erzwungene Typumwandlung, um den truthy oder falsy Wert eines Objekts zu bestimmen.

    "" == true	// false
    "" === true  // false
    
    "" == false  // true
    "" === false // false
    

Leider führt das leichter zu Fehlern, die sich gegenseitig ausschliessen und dann wieder das richtige Ausgeben und natürlich zu verwechslungen.

truthiness

Als Truthy bezeichnet man Werte die mit wahr-true verglichen werden können.

    
    if ([]) { console.log("true"); };
    // true
    
    if ({}) { console.log("true"); };
    // true
falsiness

Als Falsy bezeichnet man Werte die mit falsch-false verglichen werden können.

    if ("") { } else { console.log("false"); }
    // false

Das dreifache Gleichzeichen hingegen erwartet eine genaue Antwort.

    console.log( "" === false ); // false
    console.log( "" === "" ); // true
    console.log( false === false ); // true

Der ternäre Operator ?:

Sollte bei Ausdrücken eingesetzt werden, wo ein Wert erwartet wird und nicht zum ausführen von Funktionen. Ausnahme, man hat _genau ein Paar_ Funktionen, eine fuer true und eine fuer false, dann ist er dafür ebenfalls nützlich.

    function isAeqA() {
	return "A" === "A" ? true : false;
    }
    
    // Man kann den auch verketten
    
    var abc = "abc";
    abc === "asdfsdf" ? sayYes() : abc === "def" ? sayYes() : abc === "abc" ? sayHo() : sayYes();
    
    // Das entspricht 
    if (abc === "asdfsdf") {
        sayYes()
    } else {
	if (abc === "def") {
	    sayYes()
	} else {
	    if (abc === "abc") {
    		sayHo()
	    } else {
		sayYes()
	    }
	}
    }
    // entspricht nicht, aber ist gleich
    if (abc === "asdfsdf") {
	sayYes();
    } else if (abc === "def") {
	sayYes();
    } else if (abc === "abc") {
	sayHo();
    } else {
	sayYes();
    }	

Was leider nicht geht: { Statements } nutzen um Algorithmen auszufuhren.

Normal sollte ?: am besten zwischen name = expr?true:false oder return expr?true:false stehen. Dort wo, der eine oder andere Wert erwartet wird und nicht, um die eine oder andere Funktion auszufuehren. Da nehme man lieber if-else-if-else oder switch-case-break-default.

Update: Was aber doch geht: expr ? (f(), f(), f()) : (g(),h()); Mit runden Klammern und Kommata kann man Anweisungen zusammen ausfuehren.

    // JA
    true ? console.log("Befehl 1") : "";
    
    // NEIN
    true ? {
	console.log("Befehl 1");
	console.log("Befehl 2");
    } : console.log("false");
    

SyntaxError: Unexpected token. Ich denke mir, der ternaere Operator, der entweder das eine oder das andere zurückgibt, kann keine {} als Statement interpretieren, sondern versucht es als Objekt {} zu interpretieren...

Was aber doch geht: (f(), f(), f()) mit runden Klammern und Kommata.

	true ? (console.log("yeah"), console.log("das"), console.log("geht"), console.log("wohl")) : "";
	
	// yeah
	// das 
	// geht
	// wohl
    

Ein Statement

    // Geht immer für sich - das ist kein Objektliteral, sondern ein Statement Block.
    {
	console.log("1");
	console.log("2");
    }
    

Das Ausrufezeichen ! (Logisches NOT)

Notiz im August: Ich lese gerade das "Operatoren-Kapitel". Das sieht sowas von gekrakelt aus. Ok, ich hab das schnell gemacht, das war am Anfang. Ich muss das alles hier unbedingt nochmal schoen schreiben.

    !undefined === true

    var object = {};
    if (object) { //
	// falls object wahr ist
	console.log("defined ist truthy");
    }
    delete object;    
    if (!object) { 
	// falls object false oder undefined oder null ist
	console.log("undefined ist falsy");
    }
    object = {};
    if (!!object) { // true falls object != undefined oder false falls falsy Wert
	console.log("ich habe object definiert, damit copy und paste funktioniert");
    }
	
    function hasLocalStorage() {
	return !!window.localStorage; // Das Programm stuerzt ab, wenn window nicht definiert ist.
    }
    
    // oder so
    var capabilities = {
	storage : !!(window && window.localStorage)
    };
    
    if ( capabilities.storage ) {
	window.localStorage.setItem("KEY", "VALUE");
    }
Exkurs: Detection

!! oder Bang-Bang gibt den Boolean Wert einer Property zurueck.

Absturzsicher ist !! aber nicht. Wenn die Property und das Objekt nicht definiert sind, gibt es mit Sicherheit einen Fehler, dass die Property, die in einen Boolean verwandelt werden soll, nicht existiert. Daher...

    var system = {};

    if (typeof window != "undefined") { // !!window === true
	system.browser = true;
    }
    
    if (typeof global != "undefined") { // !!global === ERROR undefined
	system.nodejs = true;
    }

Die andere gute Idee ist der in-Operator

    if ("localStorage" in window) {
	window.localStorage.setItem("KEY", "VALUE");    
    }
    

oder halt Object.hasOwnProperty.

Um verschiedene Features testen zu können, die man nicht direkt auslesen kann, muss man zum Beispiel die vorher noetigen Objekte (zum Beispiel Elemente) erzeugen und das Feature dann ausprobieren.

~ (Bitweises NOT)

Ich glaube das ist der "Komplementaer" oder "Invertier" Operator, der alle Bits entgegendreht. Wird bei den Hoeheren Zahlen sichtbar. Hier sieht man zum Beispiel das getauschte Vorzeichenbit und die getauschen 0 und 1 bits.

    > ~2
    1
    > ~1
    0
    > ~0
    -1
    > ~1
    -2
    > ~2
    -3
    

Arrays

Ein Array in JavaScript ist ein Objekt, man sagt Array like. Ich stell mir das so vor.

    
    function MyArray() {
	for (var i = 0, j = arguments.length; i < j; i++) {
	    this.push(arguments[i]);
	}
    }
    MyArray.prototype = {
	constructor: MyArray,
	length: 0,
	push: function(value) {
	    this[this.length] = value;
	    this.length += 1;
	}
    }
    
    var array = new MyArray(1,2,3,4);
    array.length // 4
    array.push("c");
    array.length // 5
    array[5] // undefined
    array[4] // c
    array[0] // 1
    
    // Ok, mehr kann man mit der Version nicht, aber es sollte erklaeren,
    // warum ein JavaScript Array nur ein Array-like Object ist.

Array Funktionen in ECMAScript 5

Sie kamen aus den Ajaxlibraries, wie Prototype.js und sind jetzt Standard. Funktionsbasierte Iterationen.

Array.isArray

	Array.isArray = function (arr) {
	    return Object.prototype.toString.call(arr) === "[object Array]";
	};
    

Geht bei richtigen Arrays

Andere Moeglichkeiten. instanceof und der length Property Test (duck-typing).

	// geht nicht mit arguments
	if (arr instanceof Array) {
	
	}
	
	// geht mit arguments und nodelists
	// haelt aber alles mit length property fuer nen array
	if (arr && arr.length) {
	
	}
    

Die neuen indexOf und lastIndexOf Funktionen

In ES5 kann man mit der [].indexOf(X) Funktion nachgucken, ob X ein Element des Arrays ist.

	Array.prototype.indexOf = function (search, from) {
	    for (var i=from||0,len=this.length;i<len;i++)
		if (Object.hasOwnProperty.call(this, i)
		    && this[i] === search)
			return i;
	    }
	    return -1;
	};
    

Die sind per default ab ES5 vorhanden.

	["a","b","c"].indexOf("b");
	// 1
	["a","b","c"].indexOf("a");
	// 0
	["a","b","c"].indexOf("c");
	// 2
	
	["a","a","b","b","c","c"].indexOf("a");
	// 0

	["a","a","b","b","c","c"].lastIndexOf("a");
	// 1	

	["a","a","b","b","c","c"].indexOf("b");
	// 2	
	["a","a","b","b","c","c"].lastIndexOf("b");
	// 3	
    

Die lastIndexOf Funktion findet das letzte Vorkommen. Beide Funktionen geben im Mißerfolgsfalle -1 zurück.

	Array.prototype.lastIndexOf = function (search, from) {
	    for (var i=from||this.length; i>-1; i--)
		if (Object.hasOwnProperty.call(this, i)
		    && this[i] === search)
			return i;
	    }
	    return -1;
	};
    

Funktionsbasierte Iteration

Die Every Funktion ruft jedes Indiz einmal den Callback und erwartet einen !!true Wert zurück. Kriegt es einen !!true Wert zurück, wird das nächste Indiz gerufen. Ansonsten stoppt sie. Ich nutze die Funktion zum Beispiel, um zu gucken, ob alle asynchron gestarteten Funktionen in einer Reihenfolge zurückgekehrt sind.

var a = ["a","b","c","d","e","f"], ret;

ret = a.every(function(x) {	// Stoppt sobald ein falscher Wert returnt
    if (x === "d") return false; 
    console.log(x);
    return true; // oder object oder "sdfjsdf" oder 4 
});

// a
// b
// c

Die every Funktion hat man in etwa so implementiert:


    if (typeof Array.prototype.every !== "function") 
	Array.prototype.every = function (func, that) {
	    for (var i=0, len=this.length; i < len; i++) {
		if (Object.hasOwnProperty.call(this, i)
		    && !func.call(that, this[i], i, this)) return false;
	    }
	    return true;
	};

Die some Funktion erwartet !!false je Element um fortzufahren, beim ersten !!true Wert stoppt die Funktion die Interation.

ret = a.some(function(x) { // Stoppt sobald ein wahrer Wert returnt
    if (x === "d") return x; // "d" ist wie true
    console.log(x);
    return false; 
});

// a
// b
// c

// ret ist der letzte Rueckgabewert

Some wird wie every implementiert, nur wird bei einem true Wert gestoppt und bei false fortgefahren.

    if (typeof Array.prototype.some !== "function") 
	Array.prototype.some = function (func, that) {
	    for (var i=0, len=this.length; i < len; i++) {
		if (Object.hasOwnProperty.call(this, i)
		    && func.call(that, this[i], i, this)) return true;
	    }
	    return false;
	};

forEach besucht jedes Element einmal, egal, was die Funktion zurückgibt.

a.forEach(function(x) {
    console.log(x);
});
// a
// b 
// c
// d
// e
// f
    if (typeof Array.prototype.forEach !== "function") 
	Array.prototype.forEach = function (func, that) {
	    for (var i=0, len=this.length;i<len;i++) {
		if (Object.hasOwnProperty.call(this, i))
		    func.call(that, this[i], i, this);
	    }
	};

filter gibt einen neuen Array zurueck, wo alle Elemente rausgeworfen wurden, bei denen der Callback false zurückgegeben hat.

var newarray = a.filter(function(x) {
    if (x == "b" || x == "c") return false;
});

// [ "a", "d", "e", "f" ]

Filter sieht in etwa so aus.

    if (typeof Array.prototype.filter !== "function")
	Array.prototype.filter = function (func, that) {
	    var value, result = [];
	    for (var i=0, len=this.length; i<len; i++) {
		if (Object.hasOwnProperty.call(this, i)) {
		    if (func.call(that, this[i], i, this)) result.push(this[i]);
		}
	    }
	    return result;
	};

Die map Funktion gibt einen neuen Array mit den Rückgabewerten der Funktionen zurueck. Was auch undefined an stelle eines Indizes heissen soll, wo man undefined zurueckgibt. Wer das nicht will, muss mit filter kombinieren.

var newarray = a.map(function(x) {
    return "" + x + x;
});

// [ "aa", "bb", "cc", "dd", "ee", "ff" ]

Map kann man so schreiben:

    if (typeof Array.prototype.map !== "function") 
	Array.prototype.map = function (func, that) {
	    var results = [];
	    for (var i=0,len=this.length;i<len;i++) {
		if (this.hasOwnProperty(i)) {
		    result[i] = func.call(that, this[i], i, this);
		}
	    }
	    return results;
	};

Reduce arbeitet von links nach rechts, reduceRight von rechts nach links. Die Funktion gibt das Ergebnis des Callbacks als erstes Argument an den naechsten Callback weiter. Somit kann man den Array leicht aufloesen. Wie man sieht, faengt die Funktion mit den ersten Beiden Elementen an und gibt dann immer eins weiter.

console.log( [1,2,3,4,5,6,7].reduce(function (a, b) {
    console.log (a + " + "+ b + " = " + (a+b));
    return a + b;
}) );

1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
10 + 5 = 15
15 + 6 = 21
21 + 7 = 28
28

Das dritte Argument bei reduce ist die letzte Zahl und das vierte der gesamte Array

    if (typeof Array.prototype.reduce !== "function") 
	Array.prototype.reduce = function (func, initial) {
	    for (var i=0, len=this.length; i < len; i++) {
		if (Object.hasOwnProperty.call(this, i) 
		    initial = func.call(undefined, initial, this[i], i, this);
		}
	    }
	    return initial;
	};

    // reduceRight geht von rechts nach links vor.

    if (typeof Array.prototype.reduceRight !== "function") 
	Array.prototype.reduceRight = function (func, initial) {
	    for (var i=this.length; i > -1; i--) {
		if (Object.hasOwnProperty.call(this, i) 
		    initial = func.call(undefined, initial, this[i], i, this);
		}
	    }
	    return initial;
	};

Ich hoffe das hilft, man kann da einiges mit leichter schreiben, als mit for (;;), weil ich wiederholende Aufgaben nicht immer wieder neu schreiben muss. Und der Code wird dadurch lesbarer.

Hier nochmal multipleInherit mit reduceRight.


function A() { this.a = 1; }
function B() { this.b = 2; }
function C() { this.c = 3; }
function D() { this.d = 4; }

var obj = [ A, B, C, D ].reduceRight(function (a, B) {
    if (typeof a === "function") {
	a = new a();
    }
    B.prototype = a;
    return new B();
});

console.log(obj instanceof A); // true
console.log(obj instanceof B); // true
console.log(obj instanceof C); // true
console.log(obj instanceof D); // true

Obwohl es nicht schwer ist, loops zu labeln und zu brechen oder fortzufahren. Man kann sich solche Array Methoden ganz leicht auch selbst schreiben. Vielleicht auch in C++.

    // Dieses Beispiel zeigt labels bei loops an, die man mit break und continue
    // angeben kann, um gezielt die eine oder andere Schleife zu brechen oder
    // fortzufahren.

    loop1: 
    while (true) {
	console.log("while");
	
	    loop2:
	    for (var i = 10; i > 0; i--) {
		console.log("for: " +i);
		if (i === 7) continue loop2;
		if (i === 6) break loop2;
	    }	
	
	
	break loop1;
    }

Performance: In den aktuellen ES5 Browsern und Interpretern ist die Funktionsiteration als native Array Operation mittlerweile in C++ implementiert und dementsprechend schneller als zu Beginn in den Bibliotheken, wo sie, wenn ich das richtig gehört hatte, herkamen..

Typed Arrays, Buffers, Blobs

Ich glaube, darüber habe ich gar nichts geschrieben.

Mit den neuen TypedArrays kann man bequem binäre Daten transportieren, oder selbst die Grössen und Einheiten konvertieren zu müssen.

Die TypedArrays selbst stammen vom Khronos OpenGL ES (OpenGL für EcmaScript) Projekt, wenn ich mich recht erinnere.

	var rgba = new Float32Array([
	    1.0, 0.0, 0.0, 1.0,
	    0.0, 1.0, 0.0, 1.0,
	    0.0, 0.0, 1.0, 1.0
	]);
    

Es gibt da irgendwie zwei Grundgedanken. Ich muss mich aber noch ausfuehrlich informieren.

    ArrayBuffer
    -- byteLength	
    
    DataView(buffer, byteOffset, byteLength) 
    --
	float = getFloat32
	short = getInt16
	unsigned short = getUint16(byteOffset, bool LEndian)
	// usw.
    

Mit dem DataView kann man bequem einzelne Daten von offset bis length auslesen, man kann ja allerhand binaere Strukturen erhalten, und ein einfacher TypedArray reicht da nicht aus. Darum hat der DataView Methoden um Daten so aus dem ArrayBuffer zu lesen, wie man sie braucht.

Und ein paar Typen gibt es auch.

    Uint8Array
    Uint16Array
    Uint32Array
    Uint8ClampedArray // Clamped bedeutet, dass nur Werte zwischen 0 und 1 moeglich sind.
    Int8Array
    Int16Array
    Float64Array
    Int32Array
    Float32Array
    

Wer aber einen Blob (XHR2 type:'binaryData') oder Buffer (node) implementieren möchte, kann es mit den verschiedenen Typed Arrays leicht machen.

Damit lassen sich quasi alle Arten von Binaeren Daten, Video, Audio, Datastreams, herstellen.

	var x = new Uint8Array(10); // laenge 10, byteLength 10 (da bei Uint8 BYTES_PER_ELEMENT === 1)
	var dv = new DataView(x); // Der hat viele Funktionen zum Konvertieren
    

Wenn man jetzt Video und AudioDaten oder was anderes hat, kann man mit den TypedArrays was anfangen.

	var buffer = (require("buffer").Buffer)([0xC1, 0xC2]);
    

Unter NodeJS geht jede Socket Connection mit einem Buffer Hand in Hand. On data kriegt man einen Buffer. Und so weiter. Der Buffer hierbei wird eine interne Implementation in C++ sein und nicht auf TypedArrays basieren. Ist logisch.

Dann gibt es XMLHttpRequest2, welcher Blobs handlen kann, und andere HTML5 Apis, die auch Blobs handeln koennen. Hier sind auch binaere Daten im Einsatz, fuer die sich TypedArrays, sollten es nicht UTF-8 Daten sein, wunderbar lohnen.


Achtung, auch das Kapitel hat wie die anderen bereits ausgedient.

Eine ordentliche Abhandlung ueber meinen eigenen Programming-Style werde ich an dieser Stelle demnaechst bringen.

Namenskonventionen

Oder warum ich nicht immer CamelCase schreibe.

(Aber fast immer)

Die einzige wirklich wichtige Regel in JavaScript ist, eine Konstruktorfunktion mit einem Grossbuchstaben zu beginnen. So weiss man immer, dass diese Funktion mit dem new-Operator gerufen wird und ein Objekt rauskommt.

Wenn ich new Mittelfinger().zeige() anstelle von MittelFinger schreibe, ist das nicht schlimm. In Java muesste ich hingegen mittelFinger = new MittelFinger() schreiben. Weil es dort Konventionen gibt. In JavaScript ist das nicht so. In JavaScript kann man function zeigeFinger() oder function zeige_finger() schreiben

Es ist natuerlich empfohlen, in einer für andere lesbaren Art sowie camelCase zu schreiben. Da man sich weltweit fünfzehn Jahre lang mit Java rumgeplagt hat, hat sich die Notation natürlich sehr weit verbreitet. Da schreibt man Klassen gross und als Nomen, man beginnt Instanzen oder Attribute mit einem kleinen Buchstaben. Operationen sollten immer mit einem Verb anfangen, was die Tätigkeit ausdrueckt, MittelFinger mittelFinger; mittelFinger.zeigeDenFinger();.

Es gibt aber noch viele andere Programmiersprachen mit eigenen Konventionen, C/C++, Python oder Perl, PHP. In den meisten Faellen kann man aber von einer einheitlichen objektorientierten Programmierung ausgehen.

Mehr ist das nicht. In JavaScript hat man etwas mehr Freiheit. Denn es ist nicht Java. (Und hat bis auf die gekauften Namensrechte rein gar nichts damit zu tun. Auch wenn es damals ein wirklicher strategischer Schritt war, es wie Javas kleinen Bruder wirken zu lassen.)

Wichtig ist halt, klar und deutlich zu schreiben, richtig deckend zu testen, und gut zu dokumentieren, aeh, zu kommentieren. Dokumentation laesst man raus generieren..

Achja, KONSTANTEN schrieb man immer Gross. Crockford schreibt seine GLOBALS gross. Siehe YAHOO.* oder YUI.*

---

Ein paar bessere Codekonventionen für JavaScript.

Kurzes Beispiel

	if (sfs) { 
	    one_command(); 
	}

	// oder
	if (sfs) one_command();
	
	// ich nahm das erste
	// jetzt
	// nehme ich das zweite
	// (genug Klammern geschrieben, obwohl ich nur eine Zeile brauchte)
	// in C habe ich frueher auch keine Curlys genommen
    

Styleguide

Anstelle des Abschnitts Namenskonventionen. Folgt bei manueller Durcharbeit neu..

(^ Altes Zeug - )

Ich habe eine Ergaenzung fuer meinen Styleratgeber fuer mein langes JavaScript Dokument gefunden. Hier hat jemand camelCase und under_scores zusammengetan und eine Tabelle für Präfixe und Suffixe für Variablennamen nach ihren Typen aufgestellt. Die Tabelle finde ich sehr gut und Konsistenz nach Aussen (camelCase ist da die bessere Wahl) waehren man innen

In einem Talk, Single Page Web Applications JavaScript End-to-End (The Hard Stuff), kam einer der beiden Sprecher, bei den Key Concepts unter anderem darauf zu sprechen, wie sie ihren Code benennen. Und ich fand die Idee sehr gut.

Neben den mir relevanten Stilkonventionen (oder Vorschlaegen), wie

gibt es natuerlich noch ein paar andere gute Ideen. Nicht viele, aber es gibt sie.

Das hier ist eine davon, die mir auffaelt. So hat er die Caps-Lock Frage mit einer passenden Antwort geklaert.

Sie nutzen in den Projekten camelCase fuer den Module Scope und lowercase_mit_underscore für den lokalen Level, so braucht man nicht solange lesen und verstehen ueben, sondern sieht direkt ein bisschen mehr von Anfang an.

Ich versuche das mal anzuwenden, mit einem einfachen Code Skelett, wie ich schon oft verwendet habe, um etwaige Strukturen aufzudecken, als ich mit JavaScript begann. Wir sehen hier nun eine Variable, eine Funktion und eine Variable, die den return Wert einer sofort ausgefuehrten Funktion erhaelt, ein sogenanntes Modulepattern, weil diese selbstausgefuehrte Funktion nur einen Zweck erfuellt, lokale Variablen und Funktionen zur Berechnung erzeugen, moegliche Initialiser anwenden, und eine Struktur zurueckgeben, die an myModule ueberwiesen wird. Die Operationen dieser Struktur haben Zugriff auf den privaten Bereich in dem Module, auf die lokalen Funktionen. Zum Unterscheiden der Bezeichner zwischen lokaler und globaler Ebene, kommt hier die Idee mit den Cases hinzu.


    var globalVariable = "camelCased";
    
    function globalFunction () {
	var local_variable = "lower_case_with_underscore";
    }
    
    var myModule = (function () {
	var local_name;
	var local_num;
	
	function make_local ()  {
	    local_name = arguments[0];
	    local_num = Math.random();
	}    
	function get_local () {
	    return {
		localName: local_name,
		localNum: local_num
	    };
	}
	return {
	    setLocal: function (name) {
		set_local(name);
	    },
	    getLocal: function () {
		get_local();
	    }
	};
    }());
    
    myModule.setLocal("Edward");
    myModule.getLocal();

    /*
    {
	localName: "Edward",
	localNum: 0.12456786543324
    }
    */

Wie man die Variablen benennt, welchen Typen man welches Prefix oder Suffix gibt, das hat der Mann in dem Talk auch aus seiner Erfahrung vorgetragen.

Die Idee, so den Typen durchblicken zu lassen, ist Klasse. Ich hatte bislang keine Vorlage und selbst keine Tabelle im Kopf. Damit hilft mir der Mann sehr, mehr Lesbarkeit zurueckzubringen.

Type Indicator Examples Notes
Boolean sw, is, has, do is_used, sw_things, true or false
Integer int, ount, i, j, k, index, length list_length, Indicates intent, only FF uses type-inference
String name, text, title, type, key, string user_name, full_text Type and key indicate enum
Number num, n, ratio scale_ratio Always signed double fp
RegExp regex regex_match Technically an object
Array list, arr user_list Ordered list
Hash map user_map, module_map Technically an object
Object (no indicator) house_boat Traditional object with methods
jQuery Obj $ $tabs Technicall an Object
Function verb(noun) make_dog First class artifact
unknown data http_data Unknown or polymorphic

Ich habe die Tabelle fast unmodifiziert vom Bildschirm abgeschrieben, bis auf "arr" und "module_map" sind die glaube ich wortgleich. Ihr versteht, was die zeigt? Prefixe und Suffixe für Datentypen in JavaScript, wo man Anhand von var, let, const nicht erkennen kann, welcher Typ es ist.

This table is written down from a slide. Diese Tabelle habe ich jetzt einfach von seiner Slide abgeschrieben, ich fand sie so interessant, dass ich sie lernen wollen wuerde, um meinen Variablen mal ein typisiertes Praefix oder Suffix zu geben, was ich bislang nicht so mache.

Weil ich die Idee aber so gut finde, dachte ich mir, unter Nennung der Quelle natuerlich (Single Page Web Applications JavaScript End-to-End (The Hard Stuff)), selbige in meinen JavaScript Ratgeber für euch zu übernehmen.

Ich habe dieses Dokument praktisch vom DOM abgetrennt. Das gehoert eigentlich in dom.html...

CSS im Browser + JavaScript

Man kann einfach ein <style> Element nehmen

Oder die .style Property des Elements schreiben. Dann kommt man an die Referenzen auch ueber das link Objekt. Ich denke mal, das ist ein Kapitel für das DOM Dokument und gehoert hier nichtmal rein.

    var style = document.createElement("style");
    style.innerHTML = "* { outline: 1px solid red; }";
    document.head.appendChild(style);

    // Wenn man den Inhalt jetzt veraendert, veraendert sich das Dokument mit.
    // Aenderungen an StyleElementen werden vom Browser ueberwacht.

Oder Element.style zum schreiben nehmen.

    var element = document.body;
    element.style["background-color"] = "black";
    element.style.backgroundColor = "black";
    
    // Hier kann man mit [subcript] die CSS Bezeichner nutzen
    // oder mit . (Punkt) in camelCase schreiben.

    // Wegen der reserved word policy (siehe oben) gibt es im CamelCase Format
    // style.cssFloat (float ist anderswo ein Datentyp und in ES3 reserviertes Wort) 

window.getComputedStyle(element) ist zum auslesen der aktuellen Werte zu nehmen. (element.currentStyle gibt es nur in IE, von dem ich gar keine Ahnung habe).

    var readOnly = window.getComputedStyle(element);

Man kann fantastische Sachen damit machen. Es gibt aber auch ein paar Sachen zu beachten.

ACHTUNG REFLOWS: CSS per JavaScript gilt als besonders kostspielig, weil das Layout neu berechnet wird. Schreibt man bestimmte CSS Properties im .style Objekt, wird der Browser neu kalkulieren. Wer fixe Styles hat ist mit CSS Klassen besser bedient, wer Animationen will ebenfalls. Etwas ausfuehrlicher, und was ueber requestAnimationFrame schreibe ich das naechstemal.

Zur Optimierung sollte man uebrigens read-Operationen von write-Operationen trennen und jeweils gruppieren, dann kann in einem Schritt gelesen werden. Und in einem Schritt berechnet werden. Durcheinander veranlasst den Browser dauernd neu zu rechnen.

Chaining (Cascade)

Ständig this zurückgeben

Falls sie sich fragten, wie man die ganzen Funktionen aneinanderhängen kann.

    var o = {
	i: function () {
	    return this;
	},
	can: function () {
	    return this;
	},
	chain: function () {
	    return this;
	},
	everything: function () {
	    return this;
	}
    };
    o.i().can().chain().everything();

Trickreich ist auch, den Rueckgabewert einer Funktion auszulesen.

	var style = document.getElementById("grins").style;
    

Das wär aber schlechte DOM Programmierung.

Natuerlich kann man sich so von Objekt zu object chainen

	var p = {
	    more: function() {
		return this;
	    },
	    than: function() {
		return this;
	    },
	    that: function () {
		return o;
	    }
	};

	o.plus = function() {
	    return p;
	};
    
	o = o.i().can().chain().everything().plus().more().than().that();
    

Am besten ist es, hierzu noch das Kapitel CPS oder Continuation-Passing-Style, was das rufen von Callbacks zur Fortsetzung des Programms, was auch mit Sequenzen ohne asynchrone Far-Operationen geht, zu vereinen.

    // Continuation 

    function StartContinuation () {
	console.log("start");
	Continue();
    }
    function Continue () {
	console.log("continue");
	End();
    }
    function End () {
	console.log("end");
    }

    StartContinuation();
    // start
    // continue
    // end
    

Continuation Passing

Der Callback, mit dem Weitergemacht wird, der wird uebergeben.

 
    // In dem Fall habe ich eine Funktion continue dazwischen
    function continue(callback) {
	return callback();    
    }
    
    // In den meisten Faellen sind sogar verschiedene CPS moeglich.
    
    function start () {
	return continue(next);
    }
    function next() {
	return continue(end); 
    }
    function end() {
	return "Hallo Welt";
    }
    
    console.log(continue(start));
    // Hallo Welt

Async

Auch dieses Kapitel ist bereits ueberholt. Aufgrund der absoluten Richtigkeit dessen, was ich dort noch herausfinden musste und bereits zu hunderten Malen geübt habe, bleibt der Text erstmal noch so stehen, ohne dass ich glaube mich schämen zu müssen, ehe ;-)

Ganz kluger For loop mit einer asynchronen Ladeunterbrechung (Teile loops in Schritt-funktionen)

(Wahrscheinlich geeignet fuer den Spezialfall: Altes bestehendes System auf neue asynchrone Technik umarbeiten und hilft beim transformieren.)

    var args, len, waiting = 0, module = {};
    
    // Algorithmus für:
    // Mache aus 0ms nativen 'for' einige Sekunden stop and go...hehe
    
    // Sinn der asynchronen Programmierung ist eigentlich NIE ZU BLOCKIEREN.
    // Auch waehrend dieser Loop unterbrochen ist, ist eigentlich das Programm
    // laengst bei der naechten Funktion, die nach example() kommt...
    
    function process(index, code, name) {
	
	for (var i = index; i < len; i++) {
		if (module[args[i-1]]) { // letzte 
		    if (code) {
			console.log("Received: name");
			console.log(code);
			var f = new Function(code);
			f();
		    }
		} else {
		    waiting++; 	// Mit dem counter kann man festlegen, dass auf was gewartet wird
		    var xhr = new XMLHttpRequest;
		    xhr.open("GET", args[i]+".js", true);
		    xhr.onloadend = (function(idx, n) {
			return function(e) {
			    module[idx] = xhr.responseText;
			    waiting--;
			    process(idx+1, module[idx], n); 
			};
		    }(i, args[i]));
		    xhr.send(null);
		}
		if (waiting > 0) {
		    break; // (Unterbreche jetzt hier hart
		}

		// und mache nur je einen Request weiter)		
		
		// Man kann die natuerlich auch alle durcheinander ankommen
		// lassen und in der richtigen Reihenfolge z.B. mit [].every
		// auswerten. Das Beispiel kommt gleich dran.
		
		// Das hier unterbricht ein for pro Request und resumed
		// das for dann danach und prozessiert die erhaltenen Daten
		// dann gleich mit (darum die komplexen Args fuer process(...))
	}
    }

    function example() {
	args = arguments;
	len = arguments.length;
	process(0); // code + name erst beim ersten wiederaufruf
    }
    
    example("test", "mich", "jetzt");
    console.log("'Starting example does not block', sage ich.");

    // Auch waehrend dieser Loop unterbrochen ist, ist eigentlich das Programm
    // laengst bei der naechten Funktion, die nach example() kommt...

Dieses Beispiel laedt "test.js", faehrt den Loop fort, laedt "mich.js", faeht den Loop fort, laedt "jetzt.js", faehrt den Loop fort. Noch bevor test.js geladen ist, wird bereits das console.log unter example ausgefuehrt worden sein.

Uah, ich hatte drei Typos in dem Beispiel; 2 in process und 1 in example " (Quote), } (curly) und arguments.len~gth
Auch das Beispiel geht!

Counter

Man kann Zaehler nutzen, um zu gucken, ob alles gelaufen ist.

    if (received) {
	--counter;
    } else {
	++errors;
    }
    
    if (!counter && !errors) { 
	callback(all_stuff);
    } else if (errors) {
    	reset_due_to_an_error();
    } else {
	continue_waiting();
    }

Zum Beispiel mit sowas

/*
    anstatt
    var wc = waitcounters(id,key);
    wc.inc();
    gleich 
    waitcounters(id,key).inc();
    schreibt
    
*/
  var waitcounters = (function () {

    // Counter Objekt
	var waitCounter = function (value) {
	    var counter = {};
	    counter.value = value || 0;
	    counter.inc = function (value) {return this.value += value ? value : 1;};
	    counter.dec = function (value) {return this.value -= value ? value : 1;};
	    counter.done = function () {return this.value === 0;};
	    return counter;
	};

    // Utility Funktion, die es echt leicht macht.
	var counters = {};
	function wc(id, key) {
	    if (typeof id === "undefined" || typeof key === "undefined") {
			throw new TypeError("waitcounters: id and key required");
	    }
	    if (!counters[id]) counters[id]={};
	    if (!counters[id][key]) counters[id][key] = waitCounter();
	    return counters[id][key];
	}
	wc.remove = function (id, key) {
	    if (counters[id][key]) {
			counters[id][key]=null;
			delete counters[id][key];
	    }
	};
	return wc;
    }());
/*
    
    usage:

    waitcounters(id, key).inc();  // 1 oder v rauf
    waitcounters(id, key).dec(5); // 5 runter
    waitcounters(id, key).done(); // gucken ob 0
    waitcounters.remove(id,key); 	  // counter deleten

*/

Hier nochmal eine Übersicht ueber die Einfachheit des Countings.

4x numwaiting: Zaehle jeden Request einen rauf und onload einen runter und rufe den callback, wenn numwaiting 0 erreicht. 3x numloaded + 1x numexpected: Zaehle numloaded onload hoch und rufe den callback, wenn numexpected erreicht.
var waitcount = 0;
function load(f) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
	--waitcount;
	if (!waitcount) {
	    emit("fail", callback); 
	}
    };
    xhr.onerror = function () {
	emit("fail", errback); 
    };
    xhr.open("GET", f, true);
    xhr.send(null);
    ++waitcount;
};
    
var numexpected = 12; 
var numloaded = 0;
function load(f) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
	++numloaded;
	if (numloaded === numexpected) {
	    emit("done", callback); 
	}
    };
    xhr.onerror = function () {
	emit("fail", errback); 
    };
    xhr.open("GET", f, true);
    xhr.send(null);
};

"Blockieren durch Verschachteln"

Oder die Pyramide des Dooms.

Man kann diese tiefe Einklammerung auf zwei Arten betrachten...

Schlecht: Verschachtelte Asynchrone Operationen verzoegern Aufruf der naechsten Operation, dadurch, dass sie erst im Callback gerufen werden.

Oder...

Gut: Gleichzeitig ist es die einzige Moeglichkeit nacheinanderfolgende Eingaben verarbeiten zu koennen, so kommt bei der Programmierung mit asynchronen Operationen in der derzeitigen Programmierung der Flow zustande, mit dem das Programm laeuft. Das was eigentlich schlecht ist, wenn es darum geht, Berechnungen quasi paralell ablaufen lassen zu koennen, anstatt hintereinander ist natuerlich, das ist logisch, richtig, wenn es hintereinander ablaufen muss anstatt parallel

function macheWasAsync(was, callback) {
    // mach was mit was
    callback();
}

function meinAlgoTeilEins() {
    console.log("Erst ich");
}
function meinAlgoTeilZwei() {
    console.log("Dann ich");
}
function meinAlgoTeilDrei() {
    console.log("Zuletzt ich");
}

function wirrseidihrr() {
    meinAlgoTeilEins();
    macheWasAsync("dashierundrufedann", function() {
	meinAlgoTeilZwei(); 
	macheWasAsync("sdfjsldf", meinAlgoTeilDrei);	
	
	// Der "parallele" Ablauf ist hier auf jeden Fall bis zum 
	// Ende des ersten Calls blockiert (insofern laeuft das
	// 'alte Programmieren' intern etwa so im executable ab, 
	// denke ich mir da, die rufen da nacheinander call fuer
	// call, wenn der andere fertig ist) 
	
	
	// Aber, wenn das hier z.B. ein readline ist
	// oder eine andere eingabe, und ich darauf reagiere
	// und das nacheinander sein muss, ist das der einzige vernünftige Weg.
    });
}

wirrseidihrr();

Async Queues

preserving the execution order

So in etwa geht das dann, wenn man kein order! Plugin braucht, weil man vorher dran gedacht hat.

Gut - (werde ich in die Library nehmen und was neu schreiben)
function meinAlgoTeilEins() {
    console.log("1");
}
function meinAlgoTeilZwei() {
    console.log("2");
}
function meinAlgoTeilDrei() {
    console.log("3");
}

var meineAlgorithmen = [ meinAlgoTeilEins, meinAlgoTeilZwei, meinAlgoTeilDrei ];

function macheWasAsync(was_wird_fertig, callback) {
    // Code der was macht
    // Hier wird ein Algorithmusteil frei
    callback(was_wird_fertig);
}

function callback (algo_num) {
    meineAlgorithmen[algo_num].executable = true;
    // durch Every kann ich mir den "welcher ist next" Zaehler sparen,
    // weil ich durchs return false nur von 1 bis zum false komme, wenn
    // ich ab dem erste bis dahin true zurueckgebe. Die koennen also absolut
    // asynchron. oder besser gesagt, durcheinander, reinkommen
    meineAlgorithmen.every(function(algo) {
	if (algo.already_executed) { 
	    return true;
	} else if (algo.executable) { 
	    algo(); 
	    algo.already_executed = true; 
	    return true;
	} else {
	    return false;
	}
    });
}

macheWasAsync(2 /*"meinAlgoTeilDrei"*/, callback); 
macheWasAsync(1 /*"meinAlgoTeilZwei"*/, callback); 
macheWasAsync(0 /*"meinAlgoTeilEins"*/, callback); 
    
    // Der Code steht jetzt in umgekehrter Reihenfolge da
    // kann durch array.every aber nur richtigrum, wie in meineAlgorithmen
    // festgelegt, ausgefuehrt werden

    // (In diesem Beispiel wird ein 0-2 Zahlenindex benutzt, geht auch
    // mit String Keys und so...ist eure Sache :-))
    
    
// Ausgabe:
1
2
3    

Schwieriger ist das nicht. Wenn die Ausfuehrung des momentanen Threads nicht unbedingt die Daten aus dem asynchronen Call benoetigt, braucht man auch nicht (mit dem Rest der Berechnung) zu warten, sondern laesst dann weitermachen, wenn es geht.

Anderes Beispiel:

Whitespaces entfernen mit asynchronen Datei-Operationen unter node.js erfordert, weil alle mit dem Dateinamen aus dem forEach umgehen sollen, und ich nicht eine lokale Variable in Konkurrenz mit sich selbst bringen will, oder jedem File einen i-Counter verpassen, mit dem ich den Namen im Array referenziere, eine Closure. Das heisst, ich habe einen forEach Handler, in dem alle drei Handler für exists, readFile und writeFile untereinander erzeugt werden (pro File wird das einmal neu erzeugt).

 

Promises

    macheWasAsync(value)
    .then(macheNochWas)
    .then(macheDasHier)

So sieht das Beispiel der Promises/A des CommonJS Wiki aus. Das regelt auch das Common Problem, was man mit Asynchronen Algorithmen hat. Damit programmiert man nicht das, was oben steht und wrappt die Funktionen mit einem eigenen Fortgang, nein, das ist etwas anderes.

Mehr dazu spaeter, weil ich das erstmal lernen muss.


    function defer () {
	"use strict"
        var UNFULFILLED = "unfulfilled";
        var FULFILLED = "fullfilled";
        var FAILED = "failed";
        var status = UNFULFILLED;
        
        var fulfilled = [];
        var error = [];
        var progress = []; /* unbenutzt */
        var result;
        
        var promise = Object.freeze({
            status: function () {
                return status;
            },
            done: function (callback) {
		this.then(callback, null, null);
		return this;
            },
            fail: function (callback) {
            	this.then(null, callback, null);
            	return this;
            },
            then: function(fulfilledCb, errorCb, progressCb) {
                fulfilled.push(fulfilledCb);
                error.push(errorCb);
                progress.push(progressCb);
                if (status === FULFILLED) {
                    if (typeof fulfilledCb === "function") {
                	fulfilledCb.apply(promise, result);
                    }
                } else if (status === FAILED) {
                    if (typeof errorCb === "function") {
                	errorCb.apply(promise, result);
                    }
                }
                return this;
            }
        });
        
        return Object.freeze({ 
            done: function (callback) {
		promise.then(callback, null, null);
		return this;
            },
            fail: function (callback) {
            	promise.then(null, callback, null);
            	return this;
            },
            resolve: function() { 
                if (status !== UNFULFILLED) { return; }
		result = [].slice.call(arguments);                
                status = FULFILLED;
                for (var i=0; i<fulfilled.length; i++) {
                    if (typeof fulfilled[i] === "function") {
                	fulfilled[i].apply(promise, result);
                    }
                }
                return this;
            },
            reject: function(obj, args) {
                if (status !== UNFULFILLED) { return; }
                result = [].slice.call(arguments);
                status = FAILED;
                for (var i=0; i<error.length; i++) {
                    if (typeof error[i] === "function") {
                	error[i].apply(promise, result);
                    }
                }
                return this;
            },
            promise: function () {  
                return promise;
            }

        });

    };


if (typeof exports !== "undefined") {
    exports.defer = defer;
}

So sieht meine erste defer Funktion, die ein Deferred Objekt zurueckgibt, was die AsyncFunktion kriegt, die wiederrum dann "return defer.promise()" zurueckgibt, dass man ausserhalb der Funktion Callbacks zum Promise adden kann, aus! Fünf Minuten.

Faelschlicherweise hatte ich sie erst function when genannt, das ist aber eine andere Funktion, welche mehrere Promises nimmt, und wenn die alle fertig sind, dann reagiert...

    // Probieren wir es auf der Konsole aus
    
	var macheWasAsync = function (url) {
	    var deferred = defer();
	    var xhr = new XMLHttpRequest();
	    xhr.open("GET", url, true);
	    xhr.onreadystatechange = function() {
		if (xhr.readyState === 4) {
		    if (xhr.status > 199 && xhr.status < 400) {
			deferred.resolve(xhr.response);
		    } else {
			deferred.reject();
		    }
		}
	    };
	    xhr.onerror = function() {
		deferred.reject();
	    };
	    xhr.send(null);
	    return deferred.promise();
	};
	
	var promise = macheWasAsync("index.html");
	promise.then(function (data) { console.log("success:" + data); }, null, null)
	.then(null, function(error) {  console.log("promise rejected"); }, null)
	.then(function (data) { console.log("2nd success callback" + data); }, null, null);
	
	var promise2 = macheWasAsync("sfsdfsdfsdf.html");
	promise2.then(function (data) { console.log("success:" + data); }, null, null)
	.then(null, function(error) {  console.log("promise rejected"); }, null)
	.then(function (data) {}, function (err) { console.log("second error callback"); }, null);

Wie man ausprobieren kann, werden Callbacks, die nach dem Resolve oder Reject gegeben werden, dann direkt ausgefuert.

Die then Funktion verlangt je Person, die einen Callback added, immer gleich drei, wobei null oder undefined genutzt werden kann und der fehlende rest weggelassen werden.

Der Callback fuer progress ist für die Progress Events im DOM vorgesehen.

Aha, ich hab noch was fortgeschritteneres gefunden...

Eine ganz andere und fortgeschrittenere Variante des Promises, der Asynchronen Programmierung, deferred Functions, generator Functions* und so weiter gibt es auf ecmascript.org.

    function // ist das Wort für eine Generator function
    
    Dann gibt es da

-    Calling 


-    Yielding
// yield * delegates to another generator.
// im wiki kann man dann die equivalente funktion lesen

http://wiki.ecmascript.org/doku.php?id=strawman:concurrency http://wiki.ecmascript.org/doku.php?id=strawman:deferred_functions http://wiki.ecmascript.org/doku.php?id=strawman:async_functions

Rest %-Operator

Wie kriegt man even und odd (gerade und ungerade) raus?

    [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ].forEach(function(n) {
	var m = n % 2;
	
	if (m === 0) {
	    console.log(n + " ist gerade");
	} else if (m === 1) {
	    console.log(n + " is ungerade");
	}
    });

In JavaScript kriegt mit % den Rest raus.

     5 % 3 === 2
    
     5 %  14 ===  5
     5 % -14 ===  5
    -5 %  14 === -5
    -5 % -14 === -5
    
    -28 % -14 ====  0
    -29 % -14 ==== -1

Reguläre Ausdrücke

Es gibt zwei Moeglichkeiten sie zu schreiben, und mehrere Funktionen, um sie zu nutzen.

    var regex1 = /dfhsjfhsdf/g;
    var regex2 = new RegExp("sdfjhsdf","i");

Einen Ausdruck testen. Einen String auf bestimmten Inhalt prüfen.


    if (/Hallo/.test("Hallo Welt")) {
	// true
    } else {
	console.log("Tschuess Welt");
    }

Einen String matchen. (So nenn ich das.) Ergebnis ist ein Array mit dem Gesamtmatch und den () Gruppen

    // Heraus kommt ein Array mit Matches

    "Hallo Welt".match(/Hallo/);
    
    // Array [ "Hallo" ]
    
    "Hallo   Welt".match(/(Hallo)\s*(Welt)/);
    
    // Array [ "Hallo   Welt", "Hallo", "Welt" ]

Anzahl

? - Vielleicht einmal
+ - Einmal oder mehr
* - Keinmal oder mehr

Ordnung
// - Zwischen den Slashes steht der Ausdruck
[] - Zeichenmenge
-  - Von bis in einer Zeichenmenge [A-z]
() - Zusammenfassung von Zeichen und Zeichnenmengen zu einem match im Array (Gruppierung)
(abc|def) - mit | wird ein oder angezeigt
(?:) - Diese Gruppe wird nicht in den matches mitgezaehlt

// Die beiden habe ich mittlerweile wieder vergessen.
(?=) - Faehrt fort, wenn diese Gruppe folgt oder so, wenn das vorkommt
(?!) - Faehrt fort, wenn das nicht? Hab ich vergessen, guckt selbst nach, grins.

.  - Irgendein Zeichen
\. - Punkt (Backslash hebt Sonderzeichen auf, \? = ?, \+ = +, \* = *)

\w - Wortzeichen (A-z, a-z, _, 0-9
\W - Alles ausser Wortzeichen
\d - Alle Zahlen (0-9) mit Punkt und Komma
\D - Alles ausser Zahlen
\s - Leerzeichen
\S - Alles ausser Leerzeichen inklusive & = (siehe URLs)

Hinter dem Ausdruck oder als zweiter RegExp Parameter steht noch was, modifier hiessen die glaube ich im POSIX Standard,

g - globale suche
i - ignore case
m - multiline match

Was noch? Alle benutzten Zeichen muss man um sie im Ausdruck als normales Zeichen zu nutzen \fluchten

/regex/.exec(string) und eine Iteration durch alle Matches (global)

    var exp = /(?:console\.log\("([\w\s]*)"\))/g, match, code;

    function log() {
	console.log("erster satz");
	console.log("zweiter satz");
	console.log("dritter satz");
	console.log("vierter satz");
    }
    
    code = log.toString();
    
    do {
	match = exp.exec(code);
	if (match && match[1]) {
	    console.log("Fand console.log: " + match[1]);
	}
    } while (match);

    // Fand console.log: erster satz
    // Fand console.log: zweiter satz
    // Fand console.log: dritter satz
    // Fand console.log: vierter satz

Parsen ist out

Für einen URL Parser, der fast konform mit dem W3C URL Working Draft Algorithmus ist, der mir aber selbst einfiel, die ich nur noch verglichen hatte, dauerte rund zwanzig Minuten.

    function parseUrl(url) {
	var c,d, i,j, word, len,
	word = "", res = {};
	for (i = 0, len = url.length; i < len; i++) {
	    
	    c = url[i];
	    
	    if (c === ":") {
		// gucke nach //
		if (!res.scheme) {
		    res.scheme = word;
		    word = "";
		} else if (!res.host) {
		    // bin _vielleicht_ am username:password 
		    // suche erstmal nach @
		    res.username = word;
		    for (j = i; j < len; j++) {
			// nehme 
			
			d = url[j];
			if (d === "@") {
			
			} else if (d === "/") {
			    // directory, muss host sein
			    res.post
			    
			} else if (j === len -1) {
			    // ende nach port
			    res.port = word;
			    word = "";
			}
		    }
		} else if (res.host) {
		    // bin am :port
		}
	    } else if (c === "@") {

	    // Ich habe den Algorithmus, der ganz gut
	    // ist und ohne extra variablen auskommt
	    // fuer die regex vergessen.

	    } else if (c === "/") {
		// Verzeichnisse
	    }
	    else if (c === "?") {
		// query string
	    } else if (c === "#") {
		// hash
	    
	    } else {
		word = word + c;
	    }
	}
    }

Und so weiter. Kein Witz. Nach zwanzig Minuten war ich durch den Url String durch. Vier Wochen spaeter habe ich Regex geübt um die langen Parserloops in kurze Ausdrücke zu fassen. Einfach, weil es schneller und besser ausgedrueckt ist.

URL

// Der Ausdruck 

/([\w]*\:)(\/\/)?(?:([\w\d\.\-\_]*)?(?::{1}([^\s\@]*)?)?[@]{1}){0,1}([\w\d\.\-\_]*):{0,1}([\d]*){0,1}(\/[^\s\?\#]*)?(?:\?{1}([^\s\#]{0,}))?(\#[\S]*)?/

// Url

http://linux-swt.de/JavaScript.html

// Ergebnis

{ url: 'http://linux-swt.de/JavaScript.html',
  scheme: 'http:',
  slashes: '//',
  username: undefined,
  password: undefined,
  hostname: 'linux-swt.de',
  port: undefined,
  host: 'linux-swt.de',
  pathname: '/JavaScript.html',
  search: undefined,
  hash: undefined }

// Url 2

https://edward:gerhold@www.linux-swt.de:80/private/credentials.sjs?search#hash

// Ergebnis 2

{ url: 'https://edward:gerhold@www.linux-swt.de:80/private/credentials.sjs?search#hash',
  scheme: 'https:',
  slashes: '//',
  username: 'edward',
  password: 'gerhold',
  hostname: 'www.linux-swt.de',
  port: '80',
  host: 'www.linux-swt.de:80',
  pathname: '/private/credentials.sjs',
  search: "search",
  hash: "#hash" }

Issue: Ich habe den Dateinamen vergessen.

Heisst: Hinzufuegen und alle matches ab dort eine 1 addieren (als hardcoded Ziffer aber).

Keine Untergruppen in Matches, die nochmal expanden

Wenn man, nehmen wir mal den CSS Selektor, div#id.class[att="val"] und div#id.class1.class2[att="val"] hat, bleibt einem nur übrig, ".class" bzw. ".class1.class2" als ein Ergebnis zu schnappen und noch einmal zu zerlegen.

// z.B. mit 
".class1.class2".match(/\.[\w]+/g); 
// [ ".class1", ".class2" ] <= Hier kommt uebrigens keine Gruppe zum Einsatz, aber alle matches global nacheinander)
Eine dynamische Anzahl Ergebnisse habe ich mit dem Ausdruck nicht hingekriegt. Das geht glaube ich wirklich nicht. Macht jedenfalls Sinn, dass es nicht geht.

Die Gruppen koennen nicht verschoben werden um einmal eine Klasse und einmal drei Klassen zu parsen, matches.length = 9 oder = 11 geht nicht. Die ist immer gleich. Den gesamten Teilstring kann man dann nur nochmal parsen. Das ist alles, was man wissen muss. Fand ich heraus und sage, das ist so.

Ich glaube mit /g hat man sich aus einer Gruppe noch keinen Array gemacht. Dem koennte ich nochmal nachgehen.

    // Das ist ein Experiment fuer einen Selector, wo mittendrin ein Teilergebnis gleich in mehrere zerfallen muesste
    // um zu beweisen, dass man das gesamte Teilergebnis nimmt und es in einem eigenen Schritt nochmal zerlegt.
    // Der Text rundrum ist etwas umstaendlich formuliert. Ich mach das das naechstemal.

    "div.class1.class2".match(/(div)(\.[\w]+)/g);
    [ "div.class1" ]

    "div.class1.class2".match(/(div)(\.[\w]+)*/);
    [ "div.class1", "div", ".class1" ]

    "div.class1.class2".match(/(div)(\.[\w]+)*/);
    [ "div.class1.class2", "div", ".class2" ]

    "div.class1.class2".match(/(div)?(\.[\w]+)/g);
    [ "div.class1", ".class2" ]

Und so weiter, ich glaube ihr versteht. Ich denke mir, .class1.class2 nimmt man als einen String und zerlegt den nochmal

".class1.class2".match(/\.[\w]+/g); 
// [ ".class1", ".class2" ]

String

Der String wird bereits mit Quotes korrekt als String Objekt initialisiert. Man braucht die Stringklasse nicht zu nutzen, um die String Utilities zu haben.

Andere sagen sogar, man SOLL die String Klasse nicht nutzen. Man SOLL die Anführungszeichen nutzen.

HTML-Funktionen: String kann einige HTML Tags mit ausgeben.

	"Edward".link("http://linux-swt.de");
	<a href="http://linux-swt.de">Edward</a>
	
	"Edward".anchor("chapter2");
	<a name="chapter2">Edward</a>	
	
	"Edward".italics();
	<i>Edward</i>
	
	"Edward".fontsize("12px");
	<font size="12px">Edward</font>
	

sub, sup, italics, bold, fixed, fontcolor, small, strike, anchor, link sind die Namen der Stringfunktionen, die Markup mitsamt String ausgeben.

Die Funktionen gibt es nicht nur im String im Browser, sondern auch im String auf Node.js, was wohl der String von v8 wäre.

NEU in ES5 ist String.prototype.trim(), was abstehende Leerzeichen entfernt.

ECMAScript 5 bietet nun standardmaessig die trim Funktion, trim, um Strings von Leerzeichen an Anfang ^(\s*) und Ende (\s*)$ zu befreien.

	'   Edward     '.trim();
	'Edward'
	

Nützliche Stringfunktionen

	"Edward".split();
	["Edward"]				// Viel benutzt
			    
	"Edward".split("");
	["E","d","w","a","r","d"]		// String.split
						// und
	
	["E","d","w","a","r","d"].join("-");	// Array.prototype.join
	E-d-w-a-r-d
	
	"Edward".toUpperCase();
	EDWARD
	
	"Edward".toLowerCase();
	edward
	
	"Edward".charAt(2);			// Zeichen an 0-nter Pos.
	w
	
	"Edward".charCodeAt(2);			// Zeichenkode an 0-nter Pos.
	119
	

String.prototype.replace kann mit Strings und regulaeren Ausdruecken suchen, und entweder mit einem String oder mit einer Funktion ersetzen.

	"Edward".replace("Edward", "Thomas");
	Thomas

	"Thomas".replace(/Thom|s/g, function (match) {	// anderer Ausdruck, mehr Parameter
	    return match === "Thom" ? "Edw" : "rd";
	});
	Edward
	

Ich glaube die Anzahl der matches (Gruppen) des Regulaeren Ausdrucks landen in der Reihenfolge auf dem arguments Array des Replace-Callbacks.

Hier noch andere sehr häufig benutze Funktionen.

	"Edward".slice(3);
	ard
	
	"Edward".search(/ard/)			// nimmt RegExp (liefert posi 1. match)
	3
	
	"Edward".indexOf("ard")
	3
	
	"Edward".match(/ard/)			// nimmt RegExp (listet alle () in [ grup, pen ])
	[ 'ard', index: 3, input: 'Edward' ]

	"Edward".match(/Edw|ard/g);
	[ 'Edw', 'ard' ]
	
	"Edward".match(/(Edw)(ard)/);
	[ "Edward", "Edw", "ard", index: 0, input: "Edward" ]
	
	"Edward".substr(1,3);			// substr(n,m) ist eine Range
	dwa

    

NEU in ES5 ist String.prototype.trim(), was abstehende Leerzeichen ^(\s+) und (\s+)$ entfernt.

	if (typeof String.prototype.trim !== "function") 
	    String.prototype.trim = (function (re) {
		return function () {
		    return this.replace(re, "$1");
		};
	    }(/^\*(\S*(\s+\S+)*)\s*$/));
    

Number

Zahlen werden in JavaScript dezimal angezeigt. Intern hingegen ist das Format binär.

Ist Number kaputt? (Nein, das ist ein Standard)

Das man 0.1 und 0.2 nicht addieren kann, und dass das am IEEE 754 Floating Point und nicht an JavaScript liegt, weiss jeder, oder? Hier nochmal der Tipp, wie man mit Geld rechnet.

    > 0.01 + 0.02
    0.0300000004 oder wie das heisst
    
    // (a * 100 + b * 100)  / 100
    
    > (0.01 * 100 + 0.02 * 100) / 100 
    0.03
    
    // Entsprechend den Stellen nach dem Komma, die man berechnen will,
    // muss man die Formel dann um Zehnerfaktoren erweitern, ist klar,
    // oder?

Also, auf ganze Zahlen bringen, verrechnen und wieder auf Cent runter..

Beispiele mit Number

Runden auf zwei Stellen nach dem Komma.

    var n = new Number(24.43566);
    n.toFixed(2)
    // 24.44

Mit + prefixen === ->ToNumber()

Mit + vor der Variable kann man explizit in eine Number umwandeln. Nur bei doppelten + Zeichen sollte man, wegen Verwechslung mit ++ lieber Klammern rumlegen, allein der Leserlichkeit wegen.

    // String
    var a = "12";
    var b = "6";
    console.log( a + b ); // "126"

    // Number
    console.log( +a - b ); // 6

    console.log( +a + +b); // 18 (Aber! chtung hier besser Klammern nehmen)
    console.log((+a)+(+b)); // 18 (mit Klammern)

    console.log((+a)-(+b)); // 6 (mit Klammern)
    console.log(-a + +b);   // -6

    console.log(-a);	// -12
    console.log(-a + b) // -126

Die Number.isNaN Funktion ist glaube ich neu, oder erst EcmaScript next. Ist aber in NodeJS oder V8 bereits enthalten.

    
	Number.isNaN(+"abc"); // true (ergibt Not a Number)
	
	Number.isNaN(+"1"); // false (ergibt 1)
    
    

Die Number.isFinite Funktion ist genau umgekehrt. Sie zeigt an, ob es eine finite Zahl ist.

	Number.isFinite(+"abc"); // false
	
	Number.isFinite(+"1"); // true
	
    

parseInt, Number.toString

parseInt(number, radix) versucht zum radix Parameter die Eingabe zu parsen. In ES3 hatte man bei 010 auf einmal 8 oder so Sachen, weil die führende 0 für Oktalzahlen stand. Im ES5 strict mode braucht meine keine Basis 10 mehr anzugeben, auch wenn es fuer die Lesbarkeit und bei moeglicher Abwaertskompatibilitaet oder Einsatzmoeglichkeit sinnvoll ist.

    
    parseInt("010");
    // 8
    // Oktalzahl, in ES3 ein gefuerchteter Anfaengerfehler, weil
    // man nicht erwarten kann, mehrere Zahlensysteme zu koennen.
    
    
    parseInt("010", 10);
    // 10
    

Merkt euch am besten den Trick um hexadezimale und dezimale Zahlen zu konvertieren...

    // hexadezimal einlesen
    parseInt("ff", 16);
    // 255
    parseInt("ffff", 16);
    // 65535
    parseInt("ffff", 16);
    // 16777215
    
    // hexadezimal ausgebe
    Number(255).toString(16);
    // ff
    Number(65635).toString(16);
    // ffff
    Number(16777215).toString(16);
    // fffff

Der wird nuetzlich für alle HTML-Farbcodes, die bis 16777215 reichen

Math

Math ist keine Constructor Funktion, sondern ein Object. Math definiert einige Konstanten, die man öfter mal brauchen kann, sowie einige nützliche Funktionen.


EcmaScript next wird das Math Objekt um einige weitere Funktionen erweitern. Das kann man im EcmaScript.org/wiki nachlesen.

Groesste Zahl finden

    > Math.max(100,233,444,333)
    444
    
    > var n = [ 100, 233, 444, 333]
    > Math.max.apply(Math, n);
    444

Die kleinste Zahl findet man ebenso mit Math.min

Auf dem Canvas gibt es eine Arc Funktion

    // 1 Grad
    Math.PI * 2 / 360  = Math.PI / 180
    
    // deg Grad
    Math.PI/180 * deg

    // 12 Stunden
    Math.PI * 2 / 12  = Math.PI / 6
    
    // 5 hours
    Math.PI/6 * 5

Das typed array kapitel sollte ich am besten hinter number und math bewegen. number und math gelten fuer mich deutlich als unvollstaendig.

Silent Errors

ES3 hatte die Angewohnheit, ohne einen laut zu versagen.

Mehr darueber an dieser Stelle.

In ES5 strict mode gibt es statt dessen Exceptions.

Exceptions

In ES3 bekam man Silent Errors. Das Programm blieb stumm und versagte.

In ES5 (strict mode) werden dafür Exceptions verwendet. Das ist viel besser.

Wenn z.B. die Argumente falsch sind, werfe ich jetzt einen TypeError.

    var f = function (f) {
	if (typeof f !== "function" ) {
	    throw new TypeError("Hatte eine Funktion erwartet, es kam " + f);
	}
    };
    
    f(function () {});
    f("sdfsdf");
    
    // TypeError: Haette eine Funktion erwartet, es kam sdfsdf
<

Wenn sich mein Parser irrt, kann ich einen Syntax Error werfen.

    var parse = function (string) {
	var cur = string[0];
	if (cur != "{") {
	    throw new SyntaxError("Erwartete {, nicht " + cur);
	}
    };
    
    parse("{...");
    parse("...");
    

Man kann aber auch selbst irgendwas schreiben, wenn man meint, damit Erfolg zu haben.

    var MyException = function (name, message) {
	this.name = name;
	this.mesage = message;
    };
    MyException.prototype.toString = function () {
	return this.message;
    };

    throw new MyException("abc", "shdfsk jskdfhskj hskjdhfskdfh");    

Wenn man eine Ausnahme werfen will, kann man ein Error Objekt schmeissen.

    throw new Error("shit");

Oder einen String

    throw "Fuck!";

Oder ein eigenes Objekt

    String.prototype.checkMe = function () { // Bitte macht das nicht nach, irgendwelche prototypes zu extenden, lauft ihr nicht nur auf eurer Seite
	
		if (this == "shit") { // Das funktioniert nicht mit === (this.valueOf() === "shit")
		    throw {
			name: "Mist",
			code: 333,
			message: "this '==' ist gleich Mist"
		    };
		}
	    
    };

Gefangen wird es mit try-catch[-finally], wobei ein Parameter des catch Statements das geschmissene Zeugs enthaelt.

    var a = "hot", b = "shit";
	    
    a.checkMe();
	    
    try {
		b.checkMe();
    } catch (e) {
		console.error( "Ausnahme: "+ e.name +": "+ e.code +" ("+ e.message +")" );
    } finally {
	// code der grundsaetzlich noch in beiden faellen ausgefuehrt wird
    }
	
    // Ausnahme: Mist: 333 (this ist gleich Mist) 

finally wird wirklich immer ausgefuehrt.

    // finally
	try { throw {}; } catch (e) {} finally { console.log("finally"); }
	// finally
	try { } catch (e) {} finally { console.log("finally"); }
	// finally

JSLint

Wie man durch den Linter kommt

Zuerstmal komme ich nur mit Auge zu bei Tabs und Whitespace weiter. Das liegt daran, dass ich mit mcedit schreibe.

Whitespaces entfernen.

Der Linter meckert, wenn man whitespaces am Ende der Zeile hat, oder Tabs und spaces mischt, und empfiehlt selbst 4 spaces statt tab. Dieses node.js Programm macht ein Backup und ersetzt Tabs und anhängende Whitespaces.

Dies Programm für node.js demonstriert wunderbar asynchrone Fileoperationen (fuer das Kapitel async in diesem Dokument), callbacks und ihre Klammern (Closures), die mit dem Function Scope, um gemeinsame Variablen zu kennen, ohne, dass sich die Files gegenseitig beim Lesen und Schreiben der Variablen in die Quere kommen.

#!/usr/local/bin/node
// usage: node [options] jstrim.js file1.js [file2.js file3.js ...]
(function () {
    var argv = process.argv;
    var argc = argv.length;
    var filenames = argv.slice(argv.indexOf(__filename)+1, argc);
    if (filenames.length < 1) {
        console.log("usage: node [options] jstrim.js file1.js [file2.js file3.js ...]");
        process.exit();
    } else {
        console.log("node jstrim.js: convert tabs to 4 spaces and trim trailing spaces");
    }
    var fs = require("fs");
    var trailingExp = /([\s]+)$/gm;
    var tabExp = /(\t)/gm;
    var trimTrailingSpaces = function ( code ) {
        return code.toString().replace(trailingExp, "");
    };
    var replaceTabsWithSpaces = function ( code ) {
        return code.toString().replace(tabExp, "    ");
    };
    var forEachHandler = function (filename) {
        var writeHandler, readHandler, existsHandler;
        writeHandler = function (filename) {
            return function (err) {
                if (err) {
                    console.log("error: writing file"+filename);
                } else {
                    console.log("wrote: "+filename);
                }
            };
        };
        readHandler = function (err, data) {
            if (err) {
                console.log("error: cant read "+filename);
            } else if (data) {
                fs.writeFile(filename+".backup", data, "UTF-8", writeHandler(filename+".backup"));
                data = replaceTabsWithSpaces(data);
                data = trimTrailingSpaces(data);
                fs.writeFile(filename, data, "UTF-8", writeHandler(filename));
            }
        };
        existsHandler = function (exists) {
            if (exists) { fs.readFile(filename, readHandler); }
            else { console.log("error: "+filename+" does not exist"); }
        };
        fs.exists(filename, existsHandler);
    };
    filenames.forEach(forEachHandler);
}());

Trailing Comma

Das überzählige Komma am Ende von Objekten oder Arrays wurde von den meisten Browsern ignoriert, der Internet Explorer erzeugte ein weiteres leeres Feld.

	var array = [
	    "i",
	    "have",
	    "a",
	    "trailing",
	    "comma",
	];
    

Auch die Aufzählung von Objektproperties treent man mit Kommata voneinander.

	var object = {
	    i: "ich",
	    have: "habe",
	    a: "auch",
	    trailing: "ein",
	    comma: "trailendes",
	    too "Komma",
	};
    

In ES5 strict mode ist das überzählige Komma definitiv möglich und wird ignoriert. Am Besten ist aber, einfach direkt drauf zu verzichten.

Semikola

Warum function expressions ein Semikolon kriegen und function Statements nicht.


// falsch
var f = function () {

}

// richtig 
var f = function () {

};

// richtig
function A() {

}

// falsch
function A() {

};

    

Ich hatte das Semikolon hinter einer Funktion vergessen.

Bemerkt warum? Richtig, Das eine ist ein Function Statement (function ist das erste Wort bei function A () {}) , das andere eine Function Expression (var x = function () { };).

Warum ein Semikolon?

Man nehme einen Minifier


var x = function () { }
(function () { }())

// wird zu

var x=function(){}(function(){}())

Ich habe bereits erklaert, dass man nicht nur (function() {} ()); sondern auch bei einer Zuweisung var x = function () { }(); die anonyme Funktion vorher ausführen kann. Die zweite Zeile wird zum Parameter der ersten Funktion.

var x=function(){};(function(){}())

Darum ein Semikolon nach einer Funktion Expression. Function Statements sind nicht ausfuehrbar, darum schaltet man ja den Modus um in den Expression Zustand. Hier wird dann das Semikolon noetig.

Habt ihr schonmal JavaScript Programming Style und your Brain oder JavaScript: The Good Parts von Douglas Crockford gesehen? Er erklärt da noch einen Fallstrick, zu dem das Semikolon werden kann. Und zwar, wenn man die Expression nach einem return Statement nich auf die gleiche Zeile des return Statements stellt. Die Semikolon-Einsetzung setzt sofort ein ; hinter das return denn es gibt return; und das ist gleichbedeutend mit return undefined;. Es gibt undefined zurück.

    // das hier
    return 
    {
	name: ""
    };
    
    // wird zu
    return;
    {
	name: ""
    };
    
    // darum immer
    return {
	name : ""
    };
    

SublimeLinter

Sublime Text 2 Package

Vorher
Nachher

Den Sublime Text Editor 2 habe ich mir gerade aus dem Internet besorgt. Eine Vollversion oder Lizenz ist bereits für $59 erhältlich. Das Linter Package funktioniert aber und abspeichern kann ich auch. Somit eignet sich das Program erstmal auch für Hartz IV Empfänger.

Rechnet auf meinen 933MHz/512MB/32MBGrafik zwar länger als der mcedit, aber es lohnt sich. Lange brauche ich nicht, bis ich die globalen Suche und Ersetze Funktionen auswendig kann.

Kompressoren und Minifier

Das Kapitel habe ich vergessen.

  1. jsmin
  2. Closure Compiler
  3. YUI Compressor
  4. Dean Edwards Packer
  5. UglifyJS

Don't use eval?

Oder du musst Code nachladen!

Sagen wir es mal so, ich kann verstehen, wenn Crockford sagt eval("myvar = " + value); ist scheisse.

    function add(a, b) {
	return eval(a + " + " + b);
    }

Aber ich komme nicht darum rum, wenn ich vernünftig programmieren will.

    // moduleCode.js (hier vereinfacht als multiline string)
    var moduleCode =
    "define({\
    	a: "a"\
    });\
    ";

    // Beispiel
    
    var executableModule = new Function("define", moduleCode);
    executableModule.call(executableModule, MyLibrary.define);
    
    // Anstelle von

    var executionScript = document.createElement("script");
    exectionScript.textContent = moduleCode;
    document.body.appendChild(executionScript);

Beim ersten Beispiel kann define irgendeine Funktion sein, welche der evaluierenden Funktion gegeben wird. Beim zweiten Beispiel muss define eine globale Varialbe sein.

    // globale Variable define im script Tag durch wrappen
    // vermeiden

    executionScript.textContent = [ "(function (define) { ",
    moduleCode, " }(meineLib.define));" ].join("");

eval selbst hat gegenueber Function den Vorteil mit Closures umgehen zu koennen.


    // eval.js
    function code () {
	return "console.log(key);";
    }
    (function () {
	var key = "test";
	eval(code());
    }());
    
    // node eval.js
    // test

Eigentlich cool. Ich komme an die Variablen der Closure ran. Wer haette das gedacht.

Was leider nicht geht is...

    var code = function () {
	console.log(key);
    };
    (function () {
	var key = "test";
	eval(code.toString());
    }());

Sonst haette man wunderbar die Funktion des einen Objekts mit den Variablen der Closure des anderen Objekts laufen lassen koennen.

Ich denke, das geht, wenn man "function () {" und "}" von code.toString() erstmal entfernt. Ich probiere das aus und sag es euch dann.

    // Stimmt... :-) Das Beispiel geht auch mit Copy+Paste im Browser...seht selbst

        var beginFunc = /^([\s]*function[\s*]\([\S\s]*\)[\s]*\{)/;
	var endFunc = /([\s]*\}{1}[\s]*)$/m; 
    
    // indexOf und lastIndexOf waeren besser (genauer) anstatt regex-replace
    // aber ich habe das jetzt so eben ausprobiert

	var code = function (a,b,c) {
    	    var obj = {}; 
    	    console.log(key);
        };
	
	// entferne ~function () {~ und ~}~
	code.prepare = function (f) {
		var that = typeof f === "function" ? f : this; // (f) fuer das Beispiel weiter unten addiert
		var body = that.toString();
		body = body.toString().replace(beginFunc, ""); // besser noch: indexOf
		body = body.toString().replace(endFunc, ""); // besser: lastIndexOf
		return body.toString();						// und dann substr dazwischen
	};			

	(function () {
		var key="eval2-test";
		eval(code.prepare());
	}());

    // gibt "eval2-test" aus, da ich den function-Rumpf entfernt habe
    // kommt aber nicht mit inneren Funktionen klar, darum ist
    // indexOf(beginFunc) und lastIndexOf(endFunc) und substr(index bis lastindex)
    // besser und 100% wirksam.

So kann man

    var o = {
	method: function () {
	    console.log(variable);
	}
    };
    (function otherCode () {
	var variable = 12;
	eval(code.prepare(o.method));	// hierfuer habe ich (f) im Beispiel oben addiert
    }());

rufen und anders als mit .call, .apply, .bind auf die Closure zugreifen.


Der ECMAScript 5 strict mode hat sich dem eval Problem angenommen, und man kann im strict mode
nicht mehr an die Variablen der Closure ran.

Safe Eval Box

Wie kriegt man eval aus der Closure ?

    (function () {
    
	// closure 1
	// keine privaten variablen
	// evaluator hat Zugriff auf diesen Bereich.
	
	function evaluator(code) {
	    eval(code);
	}
    
    
	(function () {
	
	    // hier ist die eigentliche applikation.
	    
	    var mine1, mine2;
	    var private1, private2;
	
	    // applikation
	    // closure entfernt vom Eval 
	
	
	}());
    
    }());

Hier sind nochmal eval, der function constructor und meine safe eval closure vorgestellt.

// 1. Programme mit Eval haben eine Sicherheitsluecke
// Module Loader sind bestimmt in Gefahr.

(function () {
    var key = "hacked";
    eval("console.log(key);");
}());

// 2. Der Function Konstruktor macht nicht mit.
// Den habe ich bislang verwendet:

(function () {
    var key = "not hacked";
    var f = Function.apply(Function, ["console.log(key);"]);
    try {
    f();
    } catch (ex) { console.error(ex); }
}());

// Gerade fiel mir fuer Eval ein Trick ein.
// 3. safe eval ausserhalb der Closure

(function () {

    function evaluate(code) {
	eval(code);
    }

    (function () {
    
	var key = "also not hacked";
	try {
	evaluate("console.log(key)");
	} catch (ex) {
	    console.error(ex);
	}
    
    }());

}());

// node eval.js ergibt folgende drei Zeilen
// hacked
// [ReferenceError: key is not defined]
// [ReferenceError: key is not defined]

Seht ihr, der Trick mit der eval Closure funktioniert auch :-) Kann ich zum Kapitel safety adden.

*.toString() und *.valueOf()

Wird dort ausgegeben, wo der Stringwert oder der Wert erwartet wird.

.toString() ist immer dort zu finden, wo [object Object] usw. steht.

.valueOf() ist immer dort zu finden, wo der Wert der Variable erwartet wird.

w
    var o = {
	toString: function () {
	    return "[object o]";
	},
	valueOf: function () {
	    return "Mein Wert"; // uebrigens ein string
	}
    };
    

Wann wird .toString() gerufen und wann .valueOf() ????

    // "string" + o ruft .valueOf();
    console.log ( "Mein Wert ist " + o );
    
    // ruft o.toString();
    alert(o);
    
    var s = new String(o); // Der String Konstruktor nimmt .toString an ;-)
    
    // "[object o]"

Ich lasse damit zum Beispiel Widget-Objekte das Markup ausgeben, dass anstelle von "[object object]" das HTML erscheint.

Achso, dann gibt es noch Locale Funktionen wie toLocaleString was Zeichen und Ziffern entsprechen der Lokale Einstellung behandeln sollte.

JSON

Das wichtigste ist, dass die Keys in Quotes stehen.

	// JavaScript
	var o = {
	    names: [ "Eddie" ]
	};
     
	// JSON oder JavaScript
	{
	    "names": [ "Eddie" ]
	}
    

Man sollte den Inhalt kodieren und dekodieren

    // versand
    var obj = { a: 1, b: 2, c:3 };
    var post = Object.keys(obj).map(function(key) {
	return encodeURIComponent( key + "=" + obj[key] ); 
    });
    
    var html = " ";
    var url = "http://linux-swt.de#!/lib/mapdata.html";
    var send = {
	"host": encodeURI(url),
	"query": post.join("&"),
	"html": escape(html)
    };
    
    var n = JSON.stringify( send );
    console.log(n);
    // annahme
    var o = JSON.parse( n );
    var host = decodeURI(o.host);
    var query = o.query.split("&").map(function(q) {
	return decodeURIComponent(q);
    });
    var html = unescape(o.html);
    
    console.log(host);
    query.forEach(function(q) {
	var kv = q.match(/(.*)(?:=)(.*)/);
	console.log("key="+kv[1]+", value="+kv[2]);
    });
    
    console.log(html);
    
{"host":"http://linux-swt.de#!/lib/mapdata.html","query":"a%3D1&b%3D2&c%3D3","html":"%20"}
http://linux-swt.de#!/lib/mapdata.html
key=a, value=1
key=b, value=2
key=c, value=3

JSON gehört seit ES5 zum Standard.

	// stringifizieren
	JSON.stringify(object);
	
	// objektifizieren
	JSON.parse(string);
    

JSON.parse und JSON.stringify nehmen noch einen visit-Callback. Das wusste ich erst nicht. Gelesen habe ich das in dem Blog vom rauschma. Da gibt es noch mehr gute Beispiele. Lest mal seinen /2012/08/guide-jslang.html - Hier ist der Blog Post zur JSON API http://2ality.com/2011/08/json-api.html

function nodeVisitor (key, value) {
    console.log("key: "+key);
    console.log("value: ");
    console.dir(value);
    return value;
}

function keinKommentar (key, value) {
    console.log("key: "+key);
    console.log("value: ");
    console.dir(value);
    return 1;
}

console.log("1. Aufruf ---");
JSON.parse('{ "a": 1, "b": "b", "c": 3 }', nodeVisitor);
console.log("2. Aufruf ---");
JSON.stringify({ "a": 1, "b": "b", "c": 3 }, nodeVisitor);
console.log("3. Aufruf ---");
JSON.parse('{ "a": 1, "b": "b", "c": 3 }', keinKommentar);
console.log("4. Aufruf ---");
JSON.stringify({ "a": 1, "b": "b", "c": 3 }, keinKommentar);

1. Aufruf ---
key: a
value: 
1
key: b
value: 
'b'
key: c
value: 
3
key: 
value: 
{ a: 1, b: 'b', c: 3 }
2. Aufruf ---
key: 
value: 
{ a: 1, b: 'b', c: 3 }
key: a
value: 
1
key: b
value: 
'b'
key: c
value: 
3
3. Aufruf ---
key: a
value: 
1
key: b
value: 
'b'
key: c
value: 
3
key: 
value: 
{ a: 1, b: 1, c: 1 }
4. Aufruf ---
key: 
value: 
{ a: 1, b: 'b', c: 3 }

JSONP

"Weil man JSON Objekte und Arrays nicht direkt ausführen kann, hat man für eval einen Callback-Wrapper erfunden. Die Funktion wird dann vom Client ausgeführt."

Ich glaube mittlerweile zurueckverfolgen zu koennen, dass man den Script-Tag-Hack, so hatte Crockford den mal genannt, nutzte, um die Same-Origin-Policy zu umgehen. In eine Funktion gewrappt konnte der nachzuladende Code ausgefuehrt werden. UND ZWAR VON JEDER BELIEBIGEN SEITE.

	// Das
	<script src="http://cross.domain.org"></script>
	// ergit
	<script>
	    callback({
		"value": "much",
		"data": "more",
		"bool": true,
		"number": 5
	    });
	</script>

	<script>
	function callback(object) {
	    // Die Bandbreite reichte bis zum with Statement
	    // hiermit liessen sich Objekte in vars verwandeln. 
	    // Der State kann vom Server zum Client transformiert werden
	    // Ohne with erzeugt man die Variabel bitte so. var value = object.value;
	    with (object) { 
		console.log(value);
		console.log(data);
		console.log(bool);
		console.log(number);
	    }
	}
	</script>
	
    

Eine von vielen vielen Varianten, wie man JSONP programmieren kann.

<?php
// jsonp.php?callback=func

$callback = $_GET["callback"];
$data = Array( "names" => Array( "Edward", "Eddie", "Ed" ));
echo $callback . "('". json_encode($data) ."')";
?>
    

Das Skript gibt jetzt ein Objekt wieder, gewrappt in die Funktion, die man mit callback= spezifiziert hat

    // client

    function printUsers(data) {
	console.log("received: "+data);
	if (typeof data === "string") {
	    data = JSON.parse(data);
	}
	data.names.forEach(function(name) {
	    console.log("Got name: " + name);
	});
	return data;
    }

    var xhr = new XMLHttpRequest(), evaluate;
    xhr.open("GET", "jsonp.php?callback=printUsers", true);
    xhr.onloadend = function () {
	"use strict";
	if (xhr.responseText) {
	    evaluate = new Function(xhr.responseText);
	    // Anstelle von Function injiziert man JSONP Callbacks in Script Tags,
	    // wo der Callback auch Sinn macht, weil er beim einfuegen in den
	    // textContent des Script Tags dann evaluiert wird
	    // Der Script Tag laeuft ja sonst im globalen Context 
	    evaluate();
	}
    };
    xhr.send(null);
    
    // received: {"names":["Edward","Eddie","Ed"]}
    // Got Name: Edward
    // Got Name: Eddie
    // Got Name: Ed
    

Weiterführendes:

Mongo

JSON-Datenbank, mit JSON-P interface

    mongodb.org
    

Nachtrag: 27. August. Versuche das herauszufinden, was das und was Cont. Passing Style ist.

Proper Tail Calls

Tail Call Optimization, Tail Recursion

Ab ES6 gehoert es quasi zum Standard und Engine Implementoren sind aufgefordert, daran zu denken, um konform zu sein.

		function continue(f) {
		    return f(); // Das sind doch Tail Calls, oder?
		}
		
		var code = 
		continue(function () {
		    return continue(function () {
			return continue(function () {
			    return "Done!";
			});
		    });
		});
		
		console.log(code);
		// Done!
		

Optimierung: Falls eine Funktion, bevor sie zurueckkehrt, noch eine Funktion ruft, ist es klueger, statt erst zu springen und auf den Stack zu pushen, um dann wieder vom Stack zu poppen und zurueckzukehren, sei es sicher, einfach zum Start der zweiten Funktion zu springen, wobei die Funktion den Stack Frame von gerade verwendet.

Recursion: Hier geht es um rekursive Funktionen, die sich selbst aufrufen, ich habe das aber jetzt nicht sofort verstanden, und trage das nochmal ein, wenn ich hier weiter bin. (Rekursion selbst ist kein Problem, wenn ich Pech habe, soll ich einfach nur die bAAAAAAAeschreiben, und denke gerade an was anderes, hehe.

Wie das genau funktioniert muss ich mir erstmal beibringen.

Weiterführendes

Was genau beim TC-39 damit gemeint ist, kann man dann wohl hier lesen: http://wiki.ecmascript.org/doku.php?id=harmony:proper_tail_calls.

Continuation Passing Style

Continuation Passing Style verfolgt die Idee, am Ende einer Funktion anstelle eines return Werts eine neue Funktion zu rufen, mit der es dann weitergeht (kontinuiert).

Genaugenommen ist es das, was wir die ganze Zeit in JavaScript machen, am Ende einer Operation einen Callback rufen, um mit einem anderen Teil des Programms weiterzumachen.

    function work(cont) {
	// code...
	var more_cont = function (work) {
	    setTimeout(function(){work()},0);
	}
	cont(more_cont);
    }

Wie ich vermute scheint das aber noch nicht das Ende der Forschung auf dem Gebiet zu sein, weil mir das Gebiet hervorgehoben erschien.

Turn-based Programming

Das ist also noch nicht die Lösung, aber das wir in der Regel längst Funktionen rufen, damit das Programm weitergeht, das ist richtig...das ist Continuation-Passing-Style Mehr dazu bald.

    work(function (cont) {
	var callback = function (last) {
	    last();
	};
	cont(callback);
    });

Oh, ich habe was gefunden, was das nochmal unterstreicht und gleich besser da stehen lässt. http://2ality.com/2012/06/continuation-passing-style.html

Timeout und Interval

Die Dinger die im naechsten Tick des Eventloops im Browser (oder im Server) scheduled werden.

Ich glaube, das Beispiel hab ich bei node.js gesehen.

setTimeout("console.log('world')", 2000);
console.log("hello");
// hello
// 2sek.
// world

setTimeout und setInterval nehmen einen Callback oder einen eval-String und eine Zahl als Parameter, die den Timer in ms ausdrueckt. Zurückgegeben wird ein Objekt, was man verwenden kann um mit clearTimeout oder clearInterval den Timer zu stoppen.

    var timer = setTimeout( function () { console.log("yeah!"); } , 1000);
    clearTimeout(timer);

setInterval wiederholt den Timeout in regelmaessigen Abstaenden.

    var timer = setInterval( function () { console.log("yeah!"); } , 1000);
    clearInterval(timer);

requestAnimationFrame im Browser achtet darauf, ob das angegebene Element auf dem Bildschirm ist, oder default, ob das Tab sichtbar ist...

    function draw() {
	requestAnimationFrame(draw, canvas);
	// erstmal wird die Funktion hier verarbeitet (single thread)
	// .. 
	// ..
	// draw 
	// ..draw
	// ...draw
	// ...
    }
    requestAnimationFrame(draw, canvas);

Abbrechen kann man mit cancelAnimationFrame

Date

ES5 hat neue Datumsfunktionen, Das ISO Datumsformat ist jetzt Standard.

    console.log (new Date().toISOString());
    // 2012-09-17T22:36:50.7584
    // von Crockford
    if (typeof Date.prototype.toISOString !== "function")
	Date.prototype.toISOString = function () {
	    function fillzero(n) {
		return n < 10 ? "0"+n : n;
	    }
	    return this.getUTCFullYear()       + "-" +
		fillzero(this.getUTCMonth()+1) + "-" +
	        fillzero(this.getUTCDate())    + "T" +
		fillzero(this.getUTCHours())   + ":" +
	        fillzero(this.getUTCMinutes()) + ":" +
    		fillzero(this.getUTCSeconds()) + "Z" +
	};

Und eine .now() Funktion die für Messungen gedacht ist. Sie soll die ms einfach blitzschnell zurueckgeben. Date.now ist Standard in ES5.

    // von Crockford
    if (typeof Date.now !== "function") {
	Date.now = function () {
	    return (new Date()).getTime();
	};
    }
    
    console.log( Date.now() );
    // 134921446970

Debugging

Firebug hat alert abgelöst. Erst seit einigen Jahren gibt es vernünftige JavaScript Debugger oder das Console Interface. Davor hat man alert benutzt.

Heute gibt es eine Reihe von guten JavaScript Debuggern.

Dragonfly, Firebug, Web Inspector, node debug. Das sind die Debugger, die ich zur Verfügung habe.

Wenn man eval benutzt, kann man die Zeile nicht mehr rausfinden, dafür kann man beispielsweise den Script Tag einsetzen und damit ausfuehren. Das Skript ist dannn vollstaendig nummeriert.

Der Kommentar // @sourceURL=filename.js ermoeglicht es, Zeile und Code für evaluierten Code anzuzeigen. Der Kommentar muss in der Funktion stehen, die debuggt werden soll.

Breakpoints

Breakpoints. Man kann das Programm an bestimmten Stellen unterbrechen und inspizieren. Man kann zum Beispiel die Variableninhalte einsehen.

Man kann es auch umstaendlicher machen, und zwar so:

>Meistens benutze ich console.log zum Ausgeben von irgendwelchen Meldungen, die mich durch das Programm leiten. An unklaren Stellen schmeisse ich ein console.dir(object) ein.
    function 2beinspected() {
	//..
	//..
	console.dir(object);
	//..
	//..
    }

Das kann man sich aber auch sparen. Anstelle eine Debugmeldung einzusetzen und sie wieder zu entfernen, sollte man an der Stelle das laufende Skript kurz anhalten und die Variable, die mit console.dir(variable) angezeigt werden soll, kurz im Debugger anzugucken.

    function 2binspected() {
	//..
	//..
    X Breakpoint setzen	
	debugger; 	
	//..
	//..
    }

Was zu console.log noch zu sagen ist? Ich kann mir den Programm Flow immer wieder angucken. Unter node.js hat man die Console Funktion und auch var util = require("util"); util.debug("string"); /* gibt "DEBUG: string" aus */ und util.debug( util.inspect(object) ); was "DEBUG: string" und ein stringifiziertes Objekt ausgibt.

Web Inspector (Chrome)
Zwischen Elements und Console befindet sich der interessanteste Teil des Web Inspectors

Den Web Inspector öffnet man bequem mit F12 oder Ctrl-Shift-I oder über das Context Menu (Rechtsklick) > Inspect Element. Die Breakpoints setzt man im Scripts Frame.

Memory Leaks

Referenzen finden und loeschen

Wer kennt das nicht, man schliesst einen Programmteil ab, geht zum naechsten über, hat aber die Objekttabellen nicht aufgeräumt. Der Garbage Collector denkt, das wird noch gebraucht, weil es noch verlinkt ist.

circular references
    // Ich erzeuge hier uebrigens circular references
    // die euch von den fehlermeldungen und debuggern bekannt sein muessten

    var tonsOfData = [ 1000 ], 
	bunchOfData = [ 2000 ],
	lotsOfData = [ tonsOfData, bunchOfData, moreData ];
    var moreData = { 
	refLots: lotsOfData,
	refTons: tonsOfData
    };
    var left; 
    
    tonsOfData.push(lotsOfData);
    bunchOfData.push(tonsOfData);
    lotsOfData.push(bunchOfData);

    function grill() {
	left = [].concat(lotsOfData);
	delete lotsOfData;
    }
    grill();
    
    // Wer will da schon den Ueberblick haben, ohne Debugger?

Der Garbage Collector funktioniert erst, wenn alle Zeiger auf das Objekt entfernt werden, das Meiste findet er automatisch. Nur manchmal kann man sich nicht sicher sein, ob die Struktur nochmal benoetigt wird. Darum raeumt er sie nicht auf.

Der Internet Explorer hatte einen refCount (Reference-Count) Bug. Das heisst, dass alle Objekte die miteinander verlinkt werden, ihren refCount Zaehler erhoeht haben. Wenn man das Objekt (eine DOM-Node) nun abgelinkt hatte, wurde es doch nicht vom Garbage Collector aufgeraeumt. Der Grund war: EventListener. Da noch Listener registriert waren, die nicht ordentlich entfernt wurden, kam der refCount Zähler nicht mehr auf null runter. Die nicht gelöschten DOM Nodes, die nicht mehr im Baum waren, sammelten sich im Speicher. Da sprach man von dem bekannten IE-Memory-Leak.

Memory Management

Einen Einblick in das Memory Management von JavaScript gibt es hier.

http://developer.mozilla.org/en-US/docs/JavaScript/Memory_Management

Hmm, jetzt das gleiche nochmal in Schönschrift.

Zugriff auf globale Objekte beschraenken

Code den ihr ausfuehrt, der muss nicht alles koennen.

    // 1. Referenz holen
    windowReference = window;
    documentReference = document;
    // 2. neu setzen
    window = createWindowShim();
    document = createDocumentShim();

Das gilt aber auch nur fuer innerhalb eurer API Grenzen.

    // 3. Code der ausserhalb von eurer Box laeuft
    // hat window und document wie sonst auch.
    // Nur euer Code und (euer Custom Code den man laedt) 
    // hat das nicht. So kann man z.B. Module Loader zu einem sicheren Framework
    // ausbauen

Sicherheit

Objekt-Capabilities. Was das Objekt kann, und was du darfst. Und wie man den Zugriff aus die Capabilities des Objekts beschraenken kann.

(Das Betriebssystem erlaubt unter deinem Namen dem Programm praktisch alles)

Ein System wird durch seine Referenzen erst zu einem unsicheren System.

	// Mash-Ups sind ganz besonders insecure
    
	// http://onedomain.de/app.js
	
	(function (window) {
	    window.OurGlobal = function () {
		this.is = "good";
	    }
	    window.OurGlobal.prototype = {
		toString: function () {
		    return this.is;
		}
	    };
	
	    var og = new OurGlobal;
	    console.log(og.toString());
	    // noch good 
	    
	    setTimeout(function () {
		console.log(og.toString());
		// bad
	    }, 2000);
	}(this));
	
	
	// http://domaintwo.de/badapp.js
	(function (window) {
	    "use strict";
	    window.OurGlobal.prototype.toString = function () {
		return "bad";
	    };
	}(this));
	
	
	// jetzt kommt wieder app.js (einige Sekunden spaeter)
	// Mit setTimeout weiter oben angedeutet, dass das Programm
	// noch laueft.
	
	// console.log(og.toString());
	// bad
	
    

Man kann sie zum Beispiel durch Proxies beschraenken, die zwischen den Komponenten mediieren.

	(function (secure_global) {
	    var window = new WindowProxy(secure_global); 	    
	    (function (window, document, module) {
		"use strict";
		// eigentlicher Code
	    }.call(window, window, window.document, module));
	}(this));
    // Darstellung eines Zugriffes ueber einen Stellvertreter (skizziert von mir)
    // Das gibt es uebrigens auch und ist eins der Kapitel im Secure EcmaScript
    
    //
    // Crockford hat mit ADSafe hat eine API und eine Sandbox geschrieben.
    // Sowie eine Code Analyse um nach Brechern zu suchen
    //
    

Aeltere Notizen Erstmal sollte man im JavaScript Source Code keine CONSUMER_SECRETS oder andere vertrauliche Daten speichern. JavaScript Source Code kann praktisch von jedem eingesehen werden. Also lasst eure Credentials nicht durchblicken. Nutzt Closures, freezed die Objekte, alles was manipulierbar bleiben soll, mit Funktionen eines frozen Objekts gewrappt, in die Closure. Gib niemandem mehr Macht, als er braucht.

Security Tools für Mashups

WebWorker: Hier kommt der Vat, und der Strawman:Concurrency ins Spiel. Mehr zu Ses, was ich von Crock hab, lass ich gleich durchblicken.

Statische Analyse: ADSafe

Da gibt es ADSafe, von Douglas Crockford. Von dem ich mir die Videos angeguckt hab.

Es sucht dir den Code ab und meckert, wenn du nicht die API nutzt.

Das Ding ist eine Sandbox, wo man nur ein sicheres Subset von JavaScript benutzen darf, und DOM Access und Co nur ueber seine Interfaces hat. Ich war noch nicht gut genug, um das zu schaffen. Sollte langsam so weit sein, ich erinnere mich, mal die Site besucht zu haben, und ein bisschen geblaettert.

(Erstmal muss ich verstehen, wie das funktioniert, was ich brauche, um das zu verstehen...Ich denke ich komme mit den Docs jetzt naechstesmal weiter.)

Ich berichte, wenn ich weiter in der Sicherheit bin, dann verstehe ich, was er macht. Er gibt es in seinen Talks naemlich Preis, da muss ich nur vergleichen, ich kenn sie ja fast auswendig...

Mehr Infos und Introduction to ADSafe: http://adsafe.org

Mehr: Gears an the Mashup Problem. Video.

Caja: Code Rewriter

Caja: Spanisch für Box und Abbreviation für Ca-pability Ja-vaScript.

Da gibt es Googles Caja, was dir den Code umschreibt. Von Caja kann man lernen. Caja macht aus deinem Script ein Object Capability Safes Script, es ueberprueft alle Referenzen und schreibt sie dir um, und findet Dinge, die es mit Wrappern ersetzt, und und und. Soviel habe ich aus den Talks vom Miller verstenden. Caja habe ich bis heute nicht begriffen.

(Einschub: Ich habe ein Caja Video entdeckt. Text ein andermal. Ich verstehe Caja.)

Ich kann aber gleich schon Beispiele zeigen, wie man Secure EcmaScript auch in Caja programmieren wird, und in was für Code man umformt.

Distributed Resilient Secure EcmaScript: von den Caja Machern

Dann gibt es Dr-Ses wieder von Google. Ich glaube, in "JavaScript, your new Overlord", von Douglas Crockford, liegt der initiale Gedanke von mir, nun auch Sicherheit mit in dieses HTML file zu nehmen. Wie wichtig die ganze Programmierung dazu ist, wird mir jetzt erst bewusst, da ich erste Eindrücke davon durch ausprobieren bekommen habe.

Das ist auch die Art zu programmieren, die ich gut finde. Crockford meint auch, dass gerade Ocap Systeme auch wieder gut programmierte Systeme sind. Ich denke mir, dass stimmt. Denn über Speichersicherheit in C++, und wie ich Speicherbesetzen mit dem Professor spiele, habe ich mir schon oft ausgerechnet, als ich selbst am Speicher und dessen Sicherheit geforscht habe. Das war vor einem halben Jahrzehnt und länger.

FBJS - von Facebook

Ich hab noch keine Ahnung, habe mir gestern erst eine FB App Id besorgt.

Apropos, mit Copy und Paste hat man dann gleich fertig laufende Features...

Dr.SES

	<script src="initSES.js">
	</script>
    
http://code.google.com/p/dr-ses/

Hier bilde ich mich gleich weiter und richte das restliche Dokument und auch die anderen Sachen darauf aus, ab ES5, alles so sicher wie moeglich zu schreiben, und das heisst, Closure, Freeze, POLA, strict mode und wir sind schon fast natürlich sicher.

Object capabilities:

Capabilities, was das Objekt so kann.

Und das, was du maximal duerfen sollst.

(nicht nur safe-pointers. nein.)

JavaScript hat ein paar Klasse Patterns, um es Hackern schwer zu machen. Hinzu kommt, das JavaScript mit ES5 tamper-proof Objekte erzeugen kann, in dem sie einfach mit Object.freeze oder einem anderen Flag eingefroren werden können. ES6 shorthand wird dann const sein, das kann man in den Harmony Codes im ES-Lab bei Google lesen.

Authorization Based Access Control

Anstelle der Frage "Wer bist du?" steht die Frage "Ist das erlaubt?" voran

Mark Miller gibt eine Klasse Einfuehrung in Authorization Based Access Control.

Das gehoert zu unserem Sicherheitskonzept hinzu.

Alice, Bob, Carol

Das sind Objekte, oder Funktionen, oder Variablen, oder eine andere Entity in dem System.

Objekte haben Eigenschaften und Methoden, damit drücken sie mitunter ihr Verhalten aus. Objekte haben damit Capabilities. Und diese Faehigkeiten, die das ganze Objekt hat, gilt es zu beschraenken, indem man nur die Faehigkeiten uebergibt, die noetig sind.

Ein Objekt kann Referenzen nur auf folgenden Wegen erhalten.

	// Player1
	var Edward = {
	    address: function (name, object) {
		this[name] = object;
	    }
	};
	
	// Player2 
	var Malicious = {
	    addresses: {},
	    sniff: function (name, object) {
		this[name] = object;
	    },
	    store: function (name, object) {
		this.sniff.apply(this.addresses, arguments);
	    }
	};
	function Mallot () {	// Beim Miller kommt beim Revoker Pattern Mallot vor
	    this.name = "Malott";
	}
	Malott.prototype = Malicious; // Mal gucken, wieviele Adressen er kriegt.



	// Alice
	function Alice () { 
	    this.name = "Alice";
	    var Bob  = new Bob; 
	    var Carol = new Carol;
	}
	Alice.prototype = Edward;
	Alice.prototype.introduce = function (bobname, bob, carolname, carol) {
		// Erzeugt eine Property mit Referenz zum einen Objekt beim anderen Objekt
		bob.address(carolname, carol);	    
		carol.address(bobname, bob);
	};
	
	// Bob
	// "Bob" koennte aber auch der Name einer Funktion sein
	function Bob () {
	    this.name = "Bob";
	}
	Bob.prototype = Edward;
	
	// Carol
	// 
	function Carol () { // Carol kann auch eine Funktion oder was anderes sein
	    this.name = "Carol";
	}
	Carol.prototype = Edward;


	// Fortsetzung folgt
    
	// Bob.foo(Carol)
    

Alte Notizen. Aber das wird mal das Kapitel, wo Bob und Carol durchgehend mit Alice kommunizieren. Es gibt da eine Reihe Szenarien durchzuspielen, den Code muss ich mir selbst schreiben.

WAS DAS GENAU BEDEUTET: Ocaps [oder dt. ORefs] sind eigentlich das wichtigste Thema bezueglich EcmaScript 5. Der strict mode und die Object Meta API ermoeglichen es erst, ES in eine sichere Ocap Language zu verwandeln, aber was das genau ist, das erleutere ich euch, wie jedes der vier Worte hierueber in Kürze.

In ES3 gibt es nur die Closure. Das Objekt selbst ist nicht manipulationssicher. Man kann es einfach veraendern.

    function makeCounter () {
	    var counter = 0;
	    return {
		incr: function () { return ++counter; },
		decr: function () { return --counter; }
	    };
    }
    

In ES5 sieht es schon anders aus. Der strict mode bietet lexikalischen Scope und Object.freeze verhindert es, dass man incr oder decr veraendern kann.

	function makeCounter () {
	    "use strict";
	    var counter = 0;
	    return Object.freeze({
		incr: function () { return ++counter; },
		decr: function () { return --counter; }
	    });
	}
    

Über Object capabilities.

[ hier erscheint gleich ein Graph ]

Module: Information Hiding (Parnas)

Soviele Implementationsdetails verstecken wie moeglich. In EcmaScript macht man das mit der Closure. In anderen Sprachen mit private Properties, in ES aber mit Closures.

Security: Principles of the least Authority (POLA)

Nur soviel zulassen, wie man soll. Dafür gibt man Wrapper Funktionen zurück, die nur soviel zulassen, wie man dürfen soll.

Attenuators Compose - to attenuate heisst wohl abschwächen.

Also die Komposition eines Abschwächers. Wird in Zukunft häufiger eingesetzt.

	function makeReadOnlyFile (file) {
	    // file einrichten oder es ist bereits fertig.
	    // Gebe ein Objekt zurueck, was beschraenkte 
	    // Capabilities hat.
	    return Object.freeze({
		read: file.read,
		bytesLength: file.length
	    });
	}
	// Quelle: Mark S. Miller (siehe Videos Links)
    

Im Lazy Programmers Guide for Secure Computing gibt es ein tolles Beispiel..ich muss das aber wiederholen, wie ich merke, habe ich das Prinzip noch nicht auswendig gelernt, sonst wuesste ich es jetzt...

Den Stiegler habe ich in mehreren Videos noch gesehen. Er hat eine unglaublich laessige Art von Security zu reden. Wie er es erzaehlt ist das leicht zu lernen, von vornherein dran zu denken, es richtig zu machen, aber auch Zusammenhaenge sind leicht zu lernen bei ihm. Und der Sound ist Klasse. .

(Er meint die makeFnCaretaker)

Etwas spaeter...im anderen Vortrag kommt das gleiche vorher, es hat einen speziellen FnMaker Namen, ich melde mich demnächst endlich damit zurueck. Den Stiegler habe ich schon im April geguckt.

	
	// Das ist es nicht, aber auf sowas aehnlich laueft das raus.
	function makeChangeTable(row, cell) {
	    return function callback(value) {
		if (typeof value === "string") {
		    table[row][cell] = value;
		}
	    }
	}
	
	var chgtable = makeChangeTable(0,1);
	bob.pass(carol, chgtable);
	carol.chgtable("done");
	
	// carol kann nicht auf den richtigen table zugreifen, sondern nur
	// auf die wrapper Funktion
	
	// man schreibt sichere Wrapperfunktionen.
    

Was noch fehlt ist das Revoker Pattern.

Damit man den Table auch nur einmal changen kann.

	// Edward sein OneCall Pattern
	// Beim Design des Revokers aufgefallen
	// Es gibt ein Once Pattern
	// Die Funktion ruft changeTable 1* und nullt selbst
	// Da muss man revoke nicht selbst rufen
    
    	function makeChangeTable(row, cell) {
	    var changeTable = function (value) {
		if (typeof value === "string") {
		    table[row][cell] = value;
		}
	    };
	    return Object.freeze(function (value) {
		if (changeTable) {
		    changeTable(value);
		    changeTable = null;
		}
	    });
	}
    
    

Dr. SES ist eine linguistische Abstraktion. Momentan gibt es die Syntax nur auf Papier, also für die Spezifikation (und Patentschriften, hehe), sie benutzt ein ! (Bang) anstelle der . (Dot) Notation um eine Beziehung zu einem Methodenruf herzustellen. Es wird ein Promise zurueckgegeben.

	// Der unsichere Aufruf
	var result = bob.foo(carol);	// Local-only immediate call
	
	// Jetzt in Dr. SES
	// P steht fuer Promise
	var resultP = bobP ! foo(carol); // Eventual send
    

Diese ! Notation gibt es, wenn ich es recht verstehe, erstmal nur auf Papier. Und ich erinnere mich an den Vortrag, es geht in ES5 aber mit der Q Library. Und wenn ich mich an mich erinnere, wird in defer ein sicheres Promise erzeugt, was ein frozen Object ist. Die CommonJS Promises/B Papiere passen dazu, und auf EcmaScript.org gibt es strawman:deferred_functions, wo das Promise ebenfalls drankommt.

Folgere. Promises sind ein Teil von Dr. SES. Mit der ! Notation (auf Papier) und der Q Library (JavaScript Promise, frozen Object, when function, CommonJS Promises/B/C, etc. pp.)

Eventuell bedeutet, dass das Promise anstelle dieses Wertes steht. Man kann nun mit der Q Library dem Promise Callbacks hinzufügen, die ausgeführt werden, wenn das Promise erfüllt oder gesmashed wird.

	Q(resultP).when(function (result) {
	    // normaler callback mit einer value
	}, function (err) {
	    // normaler errback
	});
    

---

Den sicheren Callback mit dem Table habe ich auch wieder entdeckt. Also in dem einen Lazy Programmers Talk kommt das mit dem Table und die Purse dran.

In dem anderen Talk kommt der Functionforwarder und die makeMint-Purse dran.

Revocable Function Forwarder - aus dem Security Talk 2/2 vom Miller.

    // Caretaker ist von Redell von 1974 kriegt man in Paradigms Regained vom Miller beigebracht
    // 

    function makeFnCaretaker (target) {
	if (typeof target !== "function")
	    throw new TypeEror("A target function to take care for is expected as arguments[0] for makeFnCareTaker");
	return Object.freeze({
	    wrapper: function () {
		if (target) return target.apply(this, arguments);
	    },
	    revoke: function () { target = null }
	});
    }

Die Revoke Funktion dient dazu, die Verbindung, die das eine Objekt hat, die Macht, die es damit besitzt, wieder zu trennen.

Membranes - Transitive Interposition

    function makeFnMembrane ( target) {
	"use strict";
	var enabled = true;
	function wrap (wrapped) {
	    if (wrapped !== Object(wrapped)) {
		return wrapped;
	    }
	    return function () {
		if (!enabled) throw new Error("revoked");

		// return wrap(wrapped(...args.map(wrap));
		
		return wrap(wrapped.apply(this, [].slice.call(arguments).map(wrap)));
		// Da bin ich mir noch nicht sicher. Ich probiere diese Slides
		// erst noch aus und notiere mir das Kapitel Sicherheit hier (im Oktober 2012!!) gerade
	    }
	}
	return Object.freeze({
	    wrapper: wrap(target),
	    revoke: function () { enabled = false; }
	});
    }

Hier wird nicht nur die Verbindung revoked. Hier hat Alice ein Dynamit zur Hand um die Verbindung von Bob und Dave zu trennen.

Wie diese Membran funktioniert. Bald.

Das ganze Kapitel ist jetzt recht durcheinander. (Brainstorming) :-)

Rights Amplification

    function makeBrand () {
	var amp = WeakMap();
	return Object.freeze({
	    seal: function (payload) {
		var box = Object.freeze({});
		amp.set(box, payload);
		return box;
	    },
	    unseal: function (box) {
		return amp.get(box);
	    }
	});
    }

Mit Seal kann man eine Payload mit in einer Box verpacken (mit einer WeakMap registrieren, oder wie bei Crock in altem JavaScript implementiert) und mit dem Unsealer und der Box wieder rankommen. So kann die Payload nur erhalten, wer beides hat. Eine Box und einen Unsealer, aus dem Objekt, was mit der makeBrand Funktion erzeugt wurde.

Douglas Crockford erklärt den Sealer/Unsealer ebenfalls.

Mehr dazu speaer.

    function makeSealer () {
	"use strict";
	var boxes = [],
	values = [];
	return Object.freeze({
	    sealer: function (value) {
		var i = boxes.length,
		box = {};
		boxes[i] = box;
		values[i] = value;
		return box;
	    },
	    unsealer: function (box) {
		return values[boxes.indexOf(box)];
	    }
	});
    }

    // Beispiel Sealer: D. Crockford
    // Objekt.freeze: bereits gelernt und hinzugefuegt

So kann man Daten sicher durch ein System bewegen, weil man die Box und Unsealer braucht.

Die makeMint Funktion erzeugt ein sicheres EcmaScript-Portemonnaie!

    // Achtung: Das hier ist eine digitale Brieftasche. Marc Stiegler zeigt die auf einer Slide.
    // Ich praesentiere sie euch im Laufe der Uebungen dann auch mal als HTML5 Demo

    function makeMint () {
	var amp = new WeakMap();
	return function mint (balance) {
	    var purse = Object.freeze({
		getBalance: function () { return balance; },
	        makePurse: function () { return mint(0); }, //
		deposit: function (amount, src) {
		    Nat(balance + amount);
	    	    amp.get(src, Nat(amout))
		    balance += amount;
		}
	
	    });
	    function decr (amount) {
		balance = Nat(balance - amount);
	    }
	    amp.set(purse, decr);
	    return purse;
	}
    }
    
    // Von Mark S. Miller, die erklaert aber auch Marc Stiegler

    var mint = makeMint();
    var purse = mint(0);

Das ist eine secure digitale Brieftasche. Eignet sich nicht nur für echtes Geld, sondern auch als Brieftasche in Computerspielen.

Besseres Material zum Einstieg:

code.google.com/p/es-lab/

Nochmal kurz das Beispiel aus dem Eval Kapitel, das muss ich hier nochmal einordnen. Es gibt da ein Safe Eval Konzept bei Miller, ich habe aber gesehen, ich habe sowas noch nicht notiert, darum kopiere ich meinen Text kurz hierunter.

// 1. Programme mit Eval haben eine Sicherheitsluecke

(function () {
    var key = "hacked";
    eval("console.log(key);");
}());

// 2. Der Function Konstruktor macht nicht mit.

(function () {
    var key = "not hacked";
    var f = Function.apply(Function, ["console.log(key);"]);
    try {
    f();
    } catch (ex) { console.error(ex); }
}());

// 3. safe eval ausserhalb der Closure

(function () {

    function evaluate(code) {
	eval(code);
    }

    (function () {
    
	var key = "also not hacked";
	try {
	evaluate("console.log(key)");
	} catch (ex) {
	    console.error(ex);
	}
    
    }());

}());

// node eval.js ergibt folgende drei Zeilen
// hacked
// [ReferenceError: key is not defined]
// [ReferenceError: key is not defined]

JavaScript

Die populärste Programmiersprache?

// A long way to go
<script language="JavaScript" type="text/javascript" version="1.0">
    document.write("What the heck!");
</script&glt;    

Wenn ich meinen Code vom März, April, Mai mit meinem Verständnis von heute lese, fällt mir auf, dass JavaScript wirklich (durch Ausprobieren) zu erlernen ist.

    // 2000
    alert("Das nervt. Ein modaler Dialog, der jedesmal aufpoppt.");
    
    // 2010
    console.log("Das ist angenehmer...");

JavaScript selbst wird besser und die Interpreter werden immer weiter optimiert. Und ist in immer mehr Produkten verfügbar. Und immer mehr Produkte sind in JavaScript verfügbar.

JavaScript eine dieser Sprachen, um die man sich heute kümmert. Aber auch eine dieser Sprachen, die für die Leute zu erlernen ist, die nicht viel mit Programmierung zu tun haben. Man sieht es auch an den Informatikern. JavaScript ist eigentlich so einfach, dass man damit wunderbare Sachen programmieren kann.

JavaScript erreicht heute die breite Masse und viele haben Interesse daran, mit JavaScript zu programmieren. JavaScript ist jetzt auf dem Server, in Betriebssystemen, kann binäre Daten halten, auf die Graphikkarte schreiben und es gibt Hardware extra für JavaScript. JavaScript hat heute schon was geschafft, was andere Programmiersprachen nicht geschafft haben. Und was mit erfolgreichen Sprachen gemeinsamen. Einen hohen Unterhaltungswert.

Anderseits ist JavaScript, ich zitiere mal Douglas Crockford, dessen Vorträge ich mir angeschaut habe, um JavaScript zu verstehen, "the worlds most misunderstood language!".

:-)

Ecma Script next

"In eineinhalb Jahren", verstand ich. Bis das dann implementiert und verfügbar ist, dauert es dann noch ein Weilchen. Was man dann noch für Altlasten mit sich schleppt im Web, ist dann alles erst in einem Jahrzehnt richtig nutzbar.

Dennoch wird Ecma Script next ein enormer Fortschritt sein.

Ok. Hier habe ich einen Link: Anders als meine Suche nach den Flags, die jetzt folgt, führt der genau richtig zu den Harmony Proposals.

Ausprobieren: Mit flags in Chrome und Firefox. Lief bei mir letztens nicht, darum habe ich das erstmal ein paar Tage zurueckgestellt und lieber Ecma 5 gemacht. Damit kann man (ausser IE6-9) was anfangen. Aber Ecma Script next bietet Maps und Sets, das sind ganz besondere Objekte, was ich beim ersten ausprobieren nicht bemerkte, mittlerweile habe ich B. Eich reden gehoert und weiss mehr, aber noch nicht genug, um es jetzt zu sagen. Auch wie Proxies funktionieren, weiss ich seit ihm, mehr dazu sehr bald.

    // Firefox	
    <script src="next.js" type="application/JavaScript" version="next"></script>
    
    // chrome://flags Experimental JavaScript On
    // --jsflags=--harmony

Ich glaube, man kann da let (Block Scope) und Proxy (Ein Objekt was man "über ein anderes Objekt stellen kann" (falsch erklaert), Stellvertreter eben, mehr dazu später) nutzen.

Ausser var gibt es let und const. Damit kann man zwischen Function Scope und Block Scope wählen, oder halt komplett auf var verzichten, wenn man es genau nimmt, braucht man var wirklich nicht mehr, allerdings weiss ich noch nicht, ob let variablen in den let x = function sichtbar sind, wenn sie drueber stehen, ne? Muss man ausprobiert haben, oder gesagt kriegen. Aber ich denke mal, schon.

    
    var func = "whole func";		// visible everywhere in the function
    let block = "scope block";	// sichtbar im Block
    const konst = "Konstant" // read-only;

Kürzel für Funktionsnamen. Das und anderer "Sugar" verleiht JavaScript mehr Ausdruckskraft.

    var y = #(p) {
	// # anstatt function
    };

Und noch viel mehr. All das werde ich kommende Tage in diesem kurzen Kapitel vorstellen. Ich denke mir, ich darf mir die Vorträge aber nochmal reinziehen. Denn ES6 kenne ich nicht auswendig, wie ES5 bereits.

Das ist noch nicht alles.

Auf node.js gibt es IMMER v8. Das heisst, ich kann auf alle --harmony_flags zählen, die waren ebenfalls immer dabei.

Und auf ecmascript.org gibt es das reale Zeug. Die Strawman und Harmony Proposals haben noch viel viel Material inne, wie man JS gut ausstatten koennte oder noch mit wird.

Generator Functions

Ruft man auf, sie geben einen Iterator zurueck, der stoppt bei jedem yield auf das er trifft, jedesmal wenn it.next() gerufen wird. Das ist im Prinzip das, was eine Generator Function macht.

// The infinite sequence of fibonacci numbers

function* fibonacci() {
    //  ^ indicates generator function
    let [prev, curr] = [0,1];
    for (;;) {
	[prev, curr] = [curr, pref + curr];
	yield cur;
    }
}    

// Generator can be iterated over in loops (das auch)
for (n of fibonacci()) { // Beachtet den neuen for of
    if (n > 1000) break;
    print(n);
}

// Generators ARE iterators
let seq = fibonacci();
print(seq.next()); // 1
print(seq.next()); // 2
print(seq.next()); // 3
print(seq.next()); // 4

Mehr dazu gibt es auf http://wiki.ecmascript.org/doku.php?id=harmony:generators

Mit SpiderMonkey bzw. der JSSHELL kann man das ausprobieren.

// jsshell -f gen.js >> gen.js und die // vor die Resultate gesetzt:

function genfunc () {
    yield 1;
    yield 2;
    yield 3;
    yield "Ich habe die ganze Zeit die Moeglichkeit";
}
var iter = genfunc();

print(iter.next());
print(iter.next());
print(iter.next());
print(iter.next());
try {
    print(iter.next());
} catch (ex) {
    print(ex.message);
}

// 1
// 2
// 3
// Ich habe die ganze Zeit die Moeglichkeit
// undefined

for .. of

Das neue for..of kann über die Objekt Werte anstelle der Schlüßel iterieren.

var a = [ 4, 5, 6 ];
for (var b of a) {	// ueber die object values
    console.log(b);
}
// 4
// 5
// 6

// for in hatte die indices, for of hat die values
var a = [ 4, 5, 6 ];
for (var b in a) {	// ueber die object keys 0,1,2
    console.log(b);
}
// 0
// 1
// 2

Quasi Literals / Template Strings

Ein approved Feature für ES6 ist neben Generator Functions auch das Quasi Literal. Das Quasi Literal ist "Syntactic Sugar" (also auch in ES5 realisierbar, denke ich dann) welcher es erlaubt, XSS und SQL Injection Resistente DSLs oder deren Produkte zu schreiben.

    // Der String steht in Backticks.
    
    quasi`Das ist ein String mit ${domain} spezifischen ${stellen}`;
    

    

Quasi Literals hiessen vorher Template Strings. Ich denke mir, eine sehr sichere Implementation mit Regulären Ausdrücken lässt sich für jede Version damit realisieren.

Update: Heissen wohl jetzt wieder "Template Strings"

    var name = "Edward";
    console.log("Hallo ${name}");

Es gibt eine Seite, wo die String.prototype.format Funktion besprochen wird.

Ich muss mich noch einlesen, was das wird. Die neuen Template Strings oder Quasi Literals werden nämlich noch umfangreicher...

Object.is und Object.isnt

Folgt...

Array.from und Array.of

// Was ich noch bei Rauschmeyer gefunden habe
// http://www.2ality.com/2011/07/array-from.html


// Also Array.from nimmt ein Array like Objekt (NodeLists etc, mit Zahlenindex und length Property)
// und gibt einen richtigen Array zurueck...verwende ich dauernd mit arguments
// arr = Array.from(document.querySelectorAll("div"));
Array.from = function (arrayLike) {
    return [].slice.call(arrayLike);
};

// Array.of nimmt einzelne Parameter und macht einen Array raus
// arr = Array.of("a","b","c")
Array.of = function () {
    return [].slice.call(arguments);
};

// letzts Jahr wurde vorgeschlagen, diese in EcmaScript next zu nehmen
// Rick Waldron hat die erfunden und Eich hat das announced
// siehe www.2ality.com/2011/07/array-from.html

Fehlende Kapitel

Caching

Notiz an mich. Um den Memoizer, der mit Rekursion arbeitet (Crockford, ecmascript wiki), oder meinen Modulecache, oder anderes vorzustellen, fehlt noch ein Beitrag ueber Caching.

Die once Funktion wird einmal ausgefuert. Ihren Rückgabewert hält sie in einer Closure und gibt ihn bei Wiederholung wieder.

Diese once Funktion nimmt erstmal eine Funktion und gibt dann eine Funktion zurück, mit der die übergebene Funktion einmal gerufen wird, danach wird das Ergebnis aus dem Cache wiedergegeben.

// Beispiel once

var once = function( f ) {
    var cached;
    return function () {
	if (f) {
	    cached = f.apply(f, arguments);
	    f = null;
	} 
	return cached;
    };
};

var f = once(function () { return 5; });
console.log(f()); // return 5;
console.log(f()); // return cached;

Oder einmalig

var f = (function () { 
    var cached; 
    return function () {
	if (typeof cached === "undefined") {
	    // heavy computation
	    cached = 5;
	}
	return cached;
    }
}()); // immediatly invoked


f(); // chached === undefined ? cached = 5 : return cached; return cached;
f(); // return cached;

Die Memoizer Funktion wird dazu genutzt, zum Beispiel fibonacci, was auf zwei Rekursiven Funktionen zugleich basiert, und eine milliardenfache Summe an Aufrufen erzeugt, auf ein paar hundert Aufrufe runterzukriegen.

// Beispiel Memoizer

Oder mein Modulcache. Der steht stellvertretend dafür, wie einfach es ist im Script Referenzen anzulegen und Daten in einer Objekt-Tabelle zu verwalten.

// Beispiel ModuleCache

Für Caching gibt es noch ein paar Beispiele, wie z.B. Templates für HTML schonmal bereithalten.

// Beispiel Template

Sinnvoll wird Caching, wenn ein Ergebnis mehrmals gebraucht wird und man auf den Berechnungsprozess verzichten will oder kann. Dann sollte man sich einen Speicher (Cache) anlegen.

Testen

Das Kapitel muss unbedingt zu Editor, Linter, Minifier und Co.

Wie man richtige synchrone und asynchrone Tests mit JavaScript schreibt. Wie man == und === richtig zum Testen einsetzen kann. Und was man noch für Zusicherungen (Assertions) schreiben kann, um Objekte und Arrays auf ihre Gleichheit oder Ungleichheit zu prüfen. Das soll Thema dieses Kapitels sein.

// Variante 1
// speichert die Ergebnisse, throwt keinen Error

    var results = [];
    function strictEqual (actual, expected, message) {
	var pass = actual === expected;
	results.push({ test: "strictEqual", id: Date.now(), pass: pass, message: message });
    }
    print(results);

Andere Assertions zur Laufzeit sollen das Programm unterbrechen. Man erinnere sich an das ASSERT Macro in C.

// Variante 2
// speichert nichts, throwt, wenn assertion nicht erfuellt wird

    function assert (actual, expected, message) {
	if (actual === expected) return true;
	else throw new Error(message);
    }

Ein Kapitel zum Thema Testen mit JavaScript habe ich in diesem Dokument noch nicht drin.

Keine Entwicklung kommt aber ohne Test aus. Darum habe ich mir diese Notiz angelegt. Ich werde mir nun Gedanken machen, was alles in das Kapitel gehoert. Testen von Primitiven, tiefes Equal (Traversion der Keys und Values), eine Ausgabe der Testergebnisse (Browser und Console). Asynchrone Tests per CPS (Callbacks).

Als Vorbild könnte ich YUI Test, YETI, sowie das Common JS Unit Testing Framework nennen. Automatische und industrielle Testsuites, wie Selenium kann ich nicht vorstellen. Stichworte wie Code Coverage kann ich einbringen. Ich hab alle Zakas gesehen.

Ich merke mir den noch fehlenden Artikel vor.

Solange ein paar noch laufende Beispiele.

In test.html oder oldindex.html könnt ihr sehen, wie das in der Praxis bei mir zu Beginn ausgesehen hat.

Neues Testingframework

Um den Kind einen Namen zu geben, habe ich mir bereits ein Repository zugelegt. Das Unit Testing Framework muss geschrieben werden, weil ich meinen Code auch selbst testen will.

https://github.com/edwardgerhold/testing

Wenn ich weiter daran arbeite, kann ich das mit diesem Kapitel verbinden und damit gleich ein ordentliches Kapitel herstellen.

Fehlendes Kapitel

Styleguide

Styleguide: Das Kapitelchen Namenskonventionen, was so alt ist, wie das Dokument, und umreisst, dass es keine zwingende Konvention gibt, wird durch einen Styleguide (der noch nicht durchgehend in dem Dokument eingesetzt wird, sondern sich herauskristallisiert hat, und mit anderen Programmierern abgeglichen ist, ersetzt.

Dann gibt es da noch einen conventions: namespace im ecmascript.org Wiki und diese Patterns, sowie einige weitere seien zu referenzieren.

Fehlendes Kapitel

Liste Ajax Libraries

Sollte ich mal anlegen. Kann natürlich nur ein paar bekannte, grosse Libraries nennen, die mir auch so aufgefallen sind.

prototype, Moo Tools, jquery

Ember, Meteor, Angular, Knockout, Backbone, ...

Dojo, YUI, ExtJS, ...

Liste JavaScript Compiler und Transpiler

Fehlt auch noch, von Coffeescript bis Traceur.

Internationalization API

Es gibt einen separaten Ecma Standard, für eine Internationalization API. Diese umfasst eine geballte Ladung Datum & Zeit, sowie Unicode Funktionen, um Texte zu übersetzen, mit denen ich noch nie in meinem Leben gearbeitet habe.

Auf ecmascript.org: http://wiki.ecmascript.org/doku.php?id=globalization:globalization

Auf ecmascript.org: http://wiki.ecmascript.org/doku.php?id=globalization:specification_drafts

Eine bekannte Person in Zusammenhang mit Internationalisierung mit JavaScript ist Norbert Lindenberg, und auch ich habe schon mal einen Vortrag von ihm aus der yuilibrary gesehen. Ich habe da einen Link von ihm zur Internationalisierung mit JavaScript. http://norbertlindenberg.com/2012/08/javascript-internationalization/index.html. http://norbertlindenberg.com/2012/05/ecmascript-supplementary-characters/index.html http://norbertlindenberg.com/2012/10/ecmascript-internationalization-api/index.html

Was ich vor einem halben Jahr schon schrieb...

	// Speicher
	var translations = {};
	translations["de"] = {
	    "hello" : "Guten Tag!"
	};
	translations["en"] = {
	    "hello" : "Good afternoon!"
	}; // Besser Object.create(null) statt {}  nehmen, denn "constructor" ist so z.B. reserviert.
	
	// Funktion
	function translate(lang, key) {
	    return translations[lang][key];
	}
    
	// Application
	console.log(translate("de", "hello"));
	// Guten Tag!
	console.log(translate("en", "hello"));
	// Good afternoon!
    

Fehlendes Kapitel

Benchmarking

Date.now() ist eine ES5 Funktion um den aktuellen Timestamp moeglichst direkt zu erhalten.

window.navigation.performance.timing enthaelt in modernen Browsern von heute alle relevanten Browser Timestamps.

    
	function benchmark (f, iterations) {
	    var start = Date.now();
	    for (var i = 0; i < iterations; i++) {
		f();
	    }
	    var stop = Date.now();
	    console.log(iterations + "x f() = " + (stop - start) + "ms");
	}
	
	
	benchmark(function () {
	    var a = 1;
	    var b = 2;
	    var c = 3;
	    var d = (a * b * c / c / b / a) * 2;
	}, 1000);
	// 1ms
	
	benchmark(function () {
	    var a = 1;
	    var b = 2;
	    var c = 3;
	    var d = (a * b * c / c / b / a) * 2;
	}, 12000);
	// 6ms
	
	benchmark(function () {
	    var a = 1;
	    var b = 2;
	    var c = 3;
	    var d = (a * b * c / c / b / a) * 2;
	}, 333000);
	// 95ms
	
	
	benchmark(function () {
	    var a = 1;
	    var b = 2;
	    var c = 3;
	    var d = (a * b * c / c / b / a) * 2;
	}, 2000000);
	
	// 564ms 
	
	// 1,6,95,564ms
	// auf einem PIII/933MHz mit node.js 0.9.1
    

Fehlendes Kapitel

Die Dynamik und der Unterschied zwischen C++/Java und Co. und JavaScript Objekten sei nochmal erlaeutert.

Objekte

Dynamische Hashmaps

Wie man einen Array aus so einem Objekt erzeugt, und .length und push() implementiert, habe ich im Dokument schon gezeigt. Ich muss das mal zusammenfuehren...

In JavaScript kann man Objekten Properties einfach direkt zuweisen (egal welcher typ, ob string, number, boolean, array, oder objekt oder null, undefined)

	var obj = {};
	obj.a = 1;
	obj.b = 2;
	// ok
    

ausser es ist mit Object.preventExtensions(obj) davor geschützt worden.

	obj = Object.preventExtensions(obj);
	obj.c = 3; 
	// fails (c bleibt undefined);
    

In C++ haette man alle Variablen vordefinieren muessen, beziehungsweise als abstract/virtual definieren muessen.

    // C++
    #include <iostream>
    int main () {
    
	struct Object { 
	    public:
		int a;
		int b;
	    Object () {
		this.a = 1;
		this.b = 2;
	    }
	};

	Object *o; 
	o = new Object();
	std::cout << o->a << std::endl;
	std::cout << o->b << std::endl;
    }	
    

Das ist nicht so dynamisch und sehr starr und unflexibel und erfordert eine Map<string, void> im Objekt, um die Properties dynamisch zuzuweisen...

// C++    
#include <map>
#include <iostream>
#include <string>

typedef std::map<std::string, void> Properties;

int main (int argc, char **argv) {

}

// Kompilieren laesst sich das mit std::string, void, das ist gut
// Ich muss aber erstmal wieder C++ schreiben, hehe
    

Fehlendes Kapitel

Wichtige Entwurfsmuster

In Addy Osmanis Buch, was ich bereits verlinkt habe, findet man ein paar eingängige Designpatterns aus Gamma et al. und aus dem täglichen Gebrauch des Autors.

Dazu es richtig einzuüben bin ich noch nicht gekommen. Aus der Programmierung der letzten Monate habe ich aber einige Anforderungen an Entwurfsmuster, die ich noch nicht als solche in JavaScript umgesetzt habe. Das Kapitel wird folgende Patterns zeigen. Die mögen sowohl von Gamma et al., als auch von anderen stammen.

Lasst euch nicht verwirren, wenn ihr selbige bereits im Text entdeckt habt, ich habe hier halt noch viel zu schreiben und zu ordnen.

Beispiel Request auf mehreren Systemen...

Was ich hier brauche ist einen Adapter, beziehungsweise wie im folgenden Beispiel eine Fassade, beziehungsweise ein "Gemisch" (ein sauber getrenntes, bzw. kombiniertes) aus diesen und anderen Mustern.


    // Adapter Request Browser
    function requestBrowser () {
	var xhr = new XMLHttpRequest;
    }
    
    // Adapter Request Node
    function requestNode () {
	var http = require("http");
	var https = require("https");
	// ...
    }
    
    // request Fassade systemunabhaengig
    function request () {
	if (system.node) return requestNode.apply(null, arguments);
	if (system.browser) return requestBrowser.apply(null, arguments);
    }

Fortsetzung folgt.

Mediator

Der Vermittler steht praktisch zwischen mehreren Instanzen und vermittelt wortwoertlich und stellvertretend zwischen ihnen und dem System.


    var worker1, worker2, worker3; // Drei Webworker
    
    var pageMediator; // mediiert die Messages

Fortsetzung folgt.

Fehlendes Kapitel

Diagramme

UML

Halte ich für gänzlich ungeeignet, weil es sich um eine Notation für eine klassenbasierte objektorientierte Programmiersprache mit anderen Konzepten handelt. Die Notation ist nicht nur zum Teil umfangreicher, sondern zum Teil auch nicht in der Lage, JavaScript abzubilden, siehe Closures, siehe den Lambda Calculus.

DFD

Halte ich für wahrscheinlich die geeignete Notation, um mit JavaScript zu arbeiten. Aber nicht unbedingt für die Beste.

Da ich die Notation für SA/SD nicht mehr aus dem Kopf kann, werde ich erstmal nachgucken koennen, was zwischen Quelle und Senke, von Kontextdiagram, bis zur kleinsten Verfeinerung stehen kann. Die Bilder zeige ich euch dann hier.

Andere Notationen

Ich suche. Nicht "verzweifelt", aber ich "suche" (ab und zu). Aber wenn jemand was gutes tun kann, kann er eine Notation entwickeln.

Fehlendes Kapitel

TypedArrays II

TypedArrays als Memory für Dinge wie LLJS oder Emscripten.

Brendan Eich hat Cross-Compiler und 3D-Spiele gezeigt, die darauf beruhen.

Die Technik, wie ich sowas mache, bald hier.




Weiterführendes

Literatur, Videos, Links zu weiteren Quellen.

Crockford Total

Wer wirklich JavaScript programmieren will kommt um diesen Mann nicht herum, Douglas Crockford. Heute bei Paypal, vorher bei Yahoo, einst mal bei State Software. (*) Er hat die schlechten Dinge in JavaScript Identifiziert und das Beste zusammengefasst. Er bringt Lambda und Closure und Security in einer perfekten Form. Er schrieb das Code Quality Tool JSLint. Er schrieb das Securityframework ADSafe. Er unterrichtet JavaScript. Er fand einst mal JSON. Er ist im TC-39 Kommitte um die Sprache zu verbessern. Er war ES3.1 und ist ein Teil von ES5. Und das Beste ist, er kann diese Object-Capability Systeme, die nicht in seinen Einsteiger Talks vorkommen. Aber aus denen lernt man garantiert.21;5~

(*) An dieser Stelle sollte man lieber herausfinden, was Electric Communities gewesen ist. Da ich gerade erst das Gears Mashup Problem geguckt habe und erstmal Worker und Vat assoziieren gelernt habe, und gerade fasziniert von Object-Capabilities bin, finde ich, es ist genug, um kurz einen Satz einzufuegen.

JavaScript Grundlagen ES3

ECMAScript 3: Alles was man wissen muss.

Dinge die man als JavaScript Programmierer erstmal lernen muss.

Die Klasse mit gizmo und hoozit.

JavaScript Grundlagen ES3 und ES5

Eine wundervolle Reihe der Computerwissenschaften. Und natürlich JavaScript.

Viele Code Patterns die rankommen sind Ingenieursarbeit. Revoker, Promises, Memoizer. Man merkt es gar nicht.

Unverzichtbare Talks

Douglas Crockford findet man auf Youtube bei mehreren Veranstaltungen.

Secure EcmaScript (SES)

Wer es wirklich sicher will, kommt wohl hier nicht drumrum.

Mark Miller ist nicht nur im TC-39 Kommittee. Ich habe keine Ahnung, was er tut.

Er arbeitet am Caja Projekt und bei Google.

Ich habe mir die zwei Videos aus der Klasse schon 10* angeguckt und kann schon ein wenig daraus aus dem Kopf. Habe den Transfer am Schluss aber selbst noch nicht probiert zu implementieren. Ich freue mich gerade noch ueber die gefrozenen Objekte. Habe aber die Algorithmen noch nicht gelernt.

Anderes Zeug ueber Google...

Selbst noch nicht geguckt...

Brendan Eich

The Inventor of JavaScript

brendaneich.com - Achtung !!! - Super ausfuehrlich schon auf der Startseite !!!

Weitere Videos

Diese Personen sind wie Crockford, Eich und Miller auch down mit dem TC-39.




Fortsetzung folgt...Das Dokument ist mittlerweile sehr gross. Und auch der Schrott im Dokument ist damit angewachsen, aber das soll mich nicht aufhalten.

Starting in C++ Shell Mode...
Spiderman> Object.getOwnPropertyNames(this);
undefined,Function,Object,eval,Array,Boolean,Error,InternalError,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError,Math,isNaN,isFinite,parseFloat,parseInt,Number,NaN,Infinity,JSON,RegExp,escape,unescape,uneval,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,String,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,Uint8ClampedArray,ArrayBuffer,Namespace,QName,isXMLName,XML,XMLList,Iterator,StopIteration,Date,Proxy,log,exit
Spiderman> 
	

(Table of Contents? Muss erst ein Tool ran.. :) siehe nodejs.html::docsplit.js, das ist sowas in der Richtung, das kann mit dann auch alle #hrefs einsetzen, die dann fehlen, ohne mir das Dokument zu schreddern.))

top