Mein (gar nicht mal so) alter Laptop ist einigen Jahren des Dauereinsatzes physisch ganz schön am Ende. Nachdem das Ding im wirklich Wochentakt von einer Ecke des Landes an die andere geschleppt wurde ist so manches Scharnier und so mancher Anschluss ausgeleiert, eine Akkuzelle ist tot und der Sugru-Anteil an der Gesamtmasse steigt so langsam auf bedenkliche Werte. Außerdem reicht die noch heile Hardware kaum aus, um die abgefahreneren HTML5-Demos wie z.B. Epic Citadel ruckelfrei abzuspielen. Zeit also für ein neues Gerät! Gesucht war ein Ultrabook mit ordentlich Power, das linuxfreundlich und so klein und leicht wie möglich sein sollte. Geworden ist es das vollkommen bescheuert benannte Samsung 900X3E K06 ATIV Book 9 (Amazon
), ein Nachfolgemodell des bisher von mir eingesetzen (und ebenso bescheuert benannten) Samsung NP900X3A A01. Auf Twitter wurde mehrfach ein Review verlangt, das ich hiermit nach den ersten Testeinsätzen in den letzten Woche liefere.
Eigentlich macht das ATIV Book 9 alles, was ich brauche. Es hat auch für die verrückteste WebGL-Demo genug Power (i7-3537U, Intel HD 4000, 4 GB RAM) und ist dabei klein (313,8 x 218,5 x 12,9 mm) und wiegt nur 1,16 kg. Das ist noch mal ein kleine Stück kleiner und leichter als das Vorgängermodell, das von den Abmessungen her eine 1:1-Entsprechung des MacBook Air war.
Das Gehäuse ist komplett aus Metall, macht einen soliden Eindruck und sieht für einen PC auch gar nicht mal so schlimm aus. Ubuntu 13.04 ließ sich widerstandslos an die Stelle des vorinstallierten Windows 8 setzen und alles wichtige funktioniert aus dem Stand, ähnlich wie bei den Vorgängermodellen. Außer Betrieb ist bei mir im Moment die Regelung der Tastaturbeleuchtung und hin und wieder berichten nach dem Standby einzelne Indikatoren Unfug – z.B. behauptet angeschaltetes Bluetooth dass es aus ist und die Batterie zeigt nicht immer an, ob sie gerade be- oder entladen wird. Ich nehme an, dass dieser Kleinkram mit ein bisschen Frickelei zu reparieren ist oder sich mit der neuen Ubuntu-Version von selbst behebt.
Wie bei den Vorgängermodellen fällt das Ultrabook-typische, spartanische Anschluss-Angebot aus: neben 2× USB, 1× Micro-HDMI, einer kombinierten 3,5 mm-Audiobuchse und einem Kartenslot gibt es noch einen Anschluss für einen (mitgelieferten) proprietären RJ45-Adapter. Das ist nicht ungewöhnlich, aber auf der linken Seite sind die Anschlüsse so dicht aneinander gepackt, dass man dort außer dünnsten USB-Steckern nicht viel anschließen kann, wenn man nebenbei auch Strom und HDMI-Output haben möchte. Das folgende Bild zeigt die linke Seite bei voller Belegung der nun wirklich nicht besonders dicke nicht angeschlossene Speicherstick würde sich nur mit sanfter Gewalt anstelle des USB-Kabels anschließen lassen.
Als Star der Show dürfte das Display durchgehen. Es ist matt (wichtig!) und sportet mit 1.920 x 1.080 eine stattliche Auflösung. Was sich mit dieser Auflösung anstellen lässt, ist eine Software-Frage. Moderne Betriebssysteme wie Windows 8.1 sind wohl in der Lage, die Auflösung in Bildschärfe umzusetzen statt einfach alles auf dem 13,3"-Bildschirm kleiner erscheinen zu lassen. Ob man das braucht, ist wohl eine Geschmacksfrage und ob Ubuntu das kann, habe ich nicht erforscht. Mir gefällt die normale hohe Auflösung ausgesprochen gut.
Wo wurde gespart und geschlampt? Die eingebauten Lautsprecher sind natürlich nichts tolles, aber auch nicht so schlimm wie schon in anderen Laptops gehört. Die Webcam reißt ebenfalls keine Bäume aus. Und man darf natürlich nicht vergessen, dass das Gerät richtig schön teuer ist. Samsungs UVP liegt bei 1.999 € und obwohl ich mit etwas unter 1.700 € davongekommen bin, ist das nicht gerade billig. Da es an der Video-Ausgang-Front nur Micro-HDMI gibt, darf man ggf. noch zusätzlich für z.B. VGA-Adapter nicht unerhebliche Summen löhnen (ich nutze den Vorgänger dieses Teils). Fazit?
Pro:
Klein, leicht, flott
Mattes, sehr hoch auflösendes Display
Ubuntu 13.04 läuft aus dem Stand so gut wie perfekt
Contra:
Sehr eng aneinanderliegende Anschlüsse
Teuer
Ggf. müssen nicht ganz billige Micro-HDMI-Adapter gekauft werden
Für meinen Use Case (Dauermobil-Einsatz mit Ubuntu) scheint das Gerät wie gemalt zu sein. Den größten Haken würde ich eindeutig an der Kostenfront ausmachen. Man bekommt wenig Gerät (d.h. wenig Größe und Gewicht) mit angemessen viel Power für sehr viel Geld. Wer willens und in der Lage ist, etwas mehr zu schleppen oder auf das Monster-Display zu verzichten, wird sicher auch mit weniger Finanzaufwand glücklich.
In letzter Zeit hab ich hin und wieder Dinge gebastelt, die schwergewichtige Rechenaufgaben in Browsern durchführen. Dank HTML5 gibt es hierfür ja mehr als genug Anlässe, wobei mein konkretes Anliegen die Analyse von Bilddaten aus Webcam-Feeds war. Das Grundproblem bei so etwas ist, dass lang laufende Scripts (z.B. für Gesichtserkennung) den Browser komplett blockieren können. In meinem Fall ruckelte meist nur das Video, aber der Extremfall dieses Problems ist das allseits bekannte Ein-Script-auf-dieser-Seite-läuft-zu-lange-Popup. All das will man nicht haben und eigentlich hat HTML5 auch hierzu eine Lösung parat. Das Dumme an der Lösung ist, dass ihre Benutzung recht viel Aufwand bedeutet, so dass ich etwas drumherum konstruiert habe, was die Angelegenheit in vielen Fällen auf einen JavaScript-Einzeiler reduziert: Unsync.
Web Workers werden gerne als „Threads für JavaScript“ beworben. Dieses Label ist auch nicht falsch, aber ob Web Workers ihren Job durch Threads oder mit Mitteln schwarzer Magie ausführen, ist eigentlich egal. Der interessante Effekt von Workers ist, dass sie synchronen Code asynchronisieren und die Blockade des Browsers verhindern. Dieses JS-Snippet bringt (Stand Mitte 2013) selbst die neueste Chrome-Beta ins Schwitzen:
for(var i = 0; i < 50000000; i++){
Math.sqrt(Math.random() * 1000000000);
}
window.alert('Fertig!');
Bevor die Fertig-Meldung kommt, müssen wir mehrere Sekunden einen blockierten Browser ertragen. Web Workers erlauben es, die Blockade zu lösen. Dazu muss man den zeitkritischen Code in eine Extra-JS-Datei verfrachten und diese Extra-Datei durch einen Worker laden lassen:
Diese Lösung finde ich reichlich unzumutbar. Warum sollte man denn bitte den Code für die Rechenaufgabe in eine Extra-Datei verfrachten wollen? Code modularisiert man bitteschön nach rein inhaltlichen Gesichtspunkten festgelegten Grenzen, die Performance darf da nichts zu melden haben. Das ganze Gewurschtel mit Events und postMessage() könnte auch bequemer sein bzw. im Idealfall gar nicht stattfinden. Es wäre doch viel schöner wenn man einfach aus einer synchronen JS-Funktion mittels eines Einzeilers eine asnchrone Variante bauen könnte, in etwa so:
var asyncFn = macheAsynchron(synchroneFn);
asyncFn(arg1, arg2, function(result){
console.log(result);
});
Meine kleine Library namens Unsync macht genau das. Und obwohl ein ganzer Haufen HTML5-Technik zum Einsatz kommt, ist die Funktionsweise eigentlich ganz simpel und die Browserunterstützung ist auch nicht vollends katastrophal.
Wie Unsync funktioniert
Einen neuen Web Worker startet man, indem man eine URL zu einem Script in den Worker-Constructor steckt:
var myWorker = new Worker('pfad/zu/worker.js');
Es steht aber nirgends geschrieben, dass ein Script für einen Worker tatsächlich in einer eigenen Datei existieren muss … die Spezifikationen sehen nur vor, dass die Constructorfunktion eine resource identified by url übergeben bekommen soll. Und Dateiressourcen nebst URL kann man in HTML5 einfach aus heißer Luft erzeugen, File API sei Dank. Der Blob-Constructor baut uns eine ausreichend dateiartige Ressource …
var myBlob = new Blob(['Hallo Welt!'], {
type: 'text/plain'
});
… und die Funktion window.URL.createObjectURL() erzeugt eine URL auf diese Ressource, obwohl sie eigentlich nur ein JavaScript-Objekt in unserer JS-Sandbox ist:
var myBlobUrl = window.URL.createObjectURL(myBlob);
location.href = myBlobUrl;
Hiermit schickt uns der Browser an eine URL, die in etwa wie blob:http%3A//localhost/8f0a8135-ebef-43eb-b660-9ef69ce8cf3e aussieht und eine Plaintext-Datei mit dem Inhalt „Hallo Welt“ zu referenzieren scheint. Dass das Ganze nur innerhalb unserer JavaScript-Welt existiert, ist dabei für den Browser nicht von Belang – eine mit einer URL referenzierte Ressource ist eine mit einer URL referenzierte Ressource, egal woher. Und das klappt natürlich auch mit JavaScript:
var workerBlob = new Blob(['this.onmessage = function(){\
for(var i = 0; i 5000000000; i++){\
Math.sqrt(Math.random() * 1000000000);\
}\
this.postMessage(null);\
};'], {
type: 'text/javascript'
});
var workerUrl = window.URL.createObjectURL(workerBlob);
var myWorker = new Worker(workerUrl);
myWorker.postMessage(null);
myWorker.onmessage = function(){
window.alert('Fertig!')
};
… aber Code als String? Das geht ja nun mal gar nicht, schon gar nicht wenn es das performancekritische Herz unserer App ist. Lieber würde man eine Funktion in den Worker schieben. Das ist aber nicht ohne weiteres möglich – der für die Datenübermittlung in den Worker verwendete structured clone algorithm kann das nicht. Aber es gibt zum Glück Function.prototype.toString().
Funktionen haben, anders als die meisten anderen JavaScript-Objekte, eine sehr nützliche Variante der toString()-Methode. Diese spuckt tatsächlich eine brauchbare String-Repräsentation der betroffenen Funktion aus:
Warum also nicht eine normale Funktion schreiben, sie stringifizieren und das ganze als Blob bzw. Objekt-URL in einen Web Worker schieben? Anfang (function berechnung(){) und Ende (}) der Funktion stehen hierbei freilich ein wenig im Weg, denn eigentlich wollen wir bloß den Inhalt der Funktion haben, nicht die gesamte Deklaration. Zum Glück können wir die Deklaration bequem in einen sofort ausgeführten Funktionsausdruck (IIFE) verwandeln. Hierfür müssen wir bloß ein paar Klammern vorn und hinten an den Funktionscode anbringen, was leicht ist, da der Blob-Constructor ohnehin als erstes Argument ein Array von Strings erwartet. Wir nehmen also die String-Repräsentation unserer Rechnen-Funktion, hängen vorn eine und hinten drei Klammern an und stecken das ganze via Blob und Blob-URL in den Worker:
// Quellfunktion
function berechnung(){
this.onmessage = function(){
for(var i = 0; i < 5000000000; i++){
Math.sqrt(Math.random() * 1000000000);
}
this.postMessage(null);
}
}
// Quellfunktion als String
var code = berechnung.toString();
// Blob aus dem String plus Klammern für die IIFE
var workerBlob = new Blob(['(', code, ')()'], {
type: 'text/javascript'
});
var workerUrl = window.URL.createObjectURL(workerBlob);
// Worker erzeugen und benutzen
var myWorker = new Worker(workerUrl);
myWorker.postMessage(null);
myWorker.onmessage = function(){
window.alert('Fertig!');
};
Die Quellfunktion berechnung() unterliegt einigen Beschränkungen. Es wird nicht die Funktion selbst, sondern nur ein diese Funktion abbildender String in den Web Worker geschoben, was bedeutet, dass die Funktion berechnung() selbst keine Seiteneffekte/Nebenwirkungen haben kann. Sie kann zum Beispiel nicht das DOM anfassen und kann keine Variablen aus übergeordneten Scopes verwenden. Außerdem muss sie zum jetzigen Zeitpunkt noch immer für Web Workers geschrieben werden, d.h. postMessage() zur Kommunikation über die Worker-Grenzen hinweg benutzen. Und das ist doof, denn der der Autor der Quellfunktion sollte nicht wissen müssen, wie ein Worker bzw. dessen Messaging-System funktioniert. Außerdem gibt es bisher keine Möglichkeit, Daten in den Worker hinein oder aus dem Worker herauszubekommen und wenn man den Worker nach getaner Arbeit schließen und seine Blob-URL deaktivieren könnte, wäre das ebenfalls nicht verkehrt. Und ohnehin wäre das ganze als Library-Funktion außerhalb des eigentlichen Moduls viel besser aufgeboben.
Die ersten beiden Hürden sind schnell genommen; nur der String, in den die Quellfunktion eingepackt wird, muss ein bisschen angepasst werden. Die Kommunikation zwischen Web Worker und Außenwelt wird über Events abgewickelt. Worker und Außenwelt verwenden die postMessage()-Methode, um auf der jeweiligen Gegenseite ein message-Event zu triggern. Das an postMessage() übergebene Argument wird als Nachricht übertragen, was mit so gut wie jedem JS-Datentyp funktioniert (Funktionen ausgenommen). Das wegzuabstrahieren ist recht simpel: wir ersetzen einfach die Klammern, die aus dem Funktionstemplate eine IIFE machen durch Code, der die Funktion in ein message-Event einpackt, die Arguments an das Funktionstemplate weiterleitet und das Ergebnis der Funktion mittels postMessage() zurücksendet. Aus alt …
var workerBlob = new Blob(['(', code, ')()'], {
type: 'text/javascript'
});
… wird neu:
var workerBlob = new Blob([
'this.onmessage = function(evt){',
' var result = (', code, ').apply(null, evt.data);',
' this.postMessage(result);',
'};'
], {
type: 'text/javascript'
});
Jetzt kann als Quellfunktion wirklich jede Funktion herhalten, die ohne Seiteneffekte bzw. Nebenwirkungen und globale Variablen auskommt und der Autor der Funktion braucht nichts mehr über Web Workers zu wissen – dass es keine Seiteneffekte geben darf, sind die einzige verbleibende Beschränkung. Unsere Funktion berechnung() darf nun wieder aussehen wie eine ganz normale Funktion, ist schön zu lesen und durch die Verwendung im Web Worker aus nicht mehr blockierend:
// Quellfunktion
function berechnung(){
for(var i = 0; i < 500000000; i++){
Math.sqrt(Math.random() * 1000000000);
}
}
// Quellfunktion als String
var code = berechnung.toString();
// Der Rest geht von selbst :)
Nun müsste nur noch der übrige Web-Worker-spezifische Code aus unserem Modul verschwinden. Außerdem gibt es noch zwei Details, die beachtet werden wollen und die sich nicht komplett abstrahieren lassen. Mit createObjectURL() erzeugte URLs sind potenziell immerwährende Referenzen auf das betroffene Objekt und daher ein schönes Speicherleck, was auch für die Web Workers selbst gilt – wenn wir die Hintergrundprozesse nicht terminieren, tut es niemand und sie verbrauchen bis ans Ende aller Tage Speicherplatz und Rechenpower. Die Unsyc-Library versucht all das so bequem wie möglich zu gestalten.
Unsync benutzen
Unsync besteht aus eigentlich nur einer einzigen Funktion namens unsync(), die aus einer synchronen Funktion ohne Seiteneffekte/Nebenwirkungen eine äquivalente asynchrone Funktion erzeugt:
var asyncFn = unsync(fn);
asyncFn(callback);
Wenn die originale Funktion n Arguments erwartet, erwartet die asynchrone Funktion n + 1 – alle normalen Arguments plus einem Callback am Ende. Dem Callback wird als erstes Argument das von der Quellfunktion errechnete Ergebnis übergeben:
// Quellfunktion; rechnet rum und gibt ein Benchmark zurück
function crunchNumbers(x){
var startTime = new Date();
for(var i = 0; i < x; i++){
Math.sqrt(Math.random() * 1000000000);
}
var totalTime = new Date() - startTime;
return totalTime;
}
// Asynchrone Version der obrigen Funktion erzeugen
var crunchAsync = unsync(crunchNumbers);
// Async-Function mit Arguments und Callback ausrufen
crunchAsync(5000000000, function(time){
window.alert('Fertig nach ' + time);
});
So muss man fast gar nichts mehr über Web Workers wissen – fast. Das einzige, was sich nicht komplett automatsieren lässt, ist das Abschalten der Worker-Hintergrundprozesse, wenn diese nicht mehr gebraucht werden. Hierfür gibt es in Unsync zwei Möglichkeiten. Zum einem kann eine Einweg-Funktion erzeugt werden, die ihren Hintergrundprozess nach der erstmaligen Verwendung selbsttätig terminiert. Diese Funktion kann dann nur exakt einmal verwendet werden und reagiert auf neuerliche Aufruf-Versuche mit Exceptions. Um diesen Selbstzerstörungs-Mechanismus zu aktivieren einfach unsync() als zweites Argument true mitgeben:
var unsyncedEinweg = unsync(fn, true);
unsyncedEinweg(function(){ // Einmal klappt...
unsyncedEinweg(function(){}); // Exception!
});
Alternativ können mit unsync() erzeugte Funktionen über ihre terminate()-Methode manuell ihren Worker ausgeknipst bekommen. Die Eigenschaft isTerminated verrät, ob der Worker einer Funktion terminiert wurde:
var unsynced = unsync(fn, true);
unsynced(); // Berechnung startet
// Wenn wir nach einer Minute nicht fertig sind und terminiert
// haben, ist es eh zu spät...
setTimeout(function(){
if(!unsynced.isTerminated){
unsynced.terminate();
window.alert('Timeout!');
}
}, 60 * 1000);
Das wäre eigentlich alles, was es zur Benutzung Unsync zu wissen gibt, wären da nicht die Browser bzw. die zwei ausgesuchten Prachtexemplare ihrer Zunft.
Wo ist der Haken?
Alle halbwegs aktuellen Versionen aller relevanten Browser unterstützen die nötigen APIs … bis auf den Android-Browser, der keine Web Workers kennt. Die IE 10 und 11 unterstützen zwar alle APIs, wissen aber anscheinend nicht, dass Blob-URLs dem gleichen Origin wie die sie erzeugende Webseite zuzuordnen sind. Für den IE gelten alle Blob-URLs gefährlicher Schadcode von außerhalb, der nicht ausgeführt werden darf. Das macht die komplette Objekt-URL-API im IE ziemlich nutzlos und verhindert natürlich auch, dass Unsync dort funktioniert. Es gibt mehrere Bug Reports zu dem Thema (das ist meiner, falls ihr ein paar me too-Kommentare loswerden wollt), aber Microsoft macht bisher nicht den Eindruck, als wäre ein Fix für den finalen IE 11 angedacht. Da dürfen wir wohl auf den IE 12 warten. Bis dahin gilt:
Browser
Chrome
Firefox
Safari
Opera
iOS
Android
IE
Unterstützung
✓
✓
✓
✓
✓
✕
✕
Bis dahin könnt ihr Unsync eigentlich in der Pfeife rauchen und dürft weiterhin mit Extra-Dateien für Web Workers jonglieren. Aber immerhin besteht zumindest für eure Enkel Hoffnung auf bequeme asynchrone Funktionen im IE 18 … und das ist doch auch schon mal was, oder?
Ich bin nicht der einzige, der sich in den letzten Wochen auf Twitter kritisch über billige IE-Witze geäußert hat. Schließlich scheint Microsoft ja mittlerweile begriffen zu haben, wie man einen Browser baut und die Internet Explorer 10 und 11 haben so viel HTML5 und CSS3 an Bord, dass man auf den ersten Blick vergleichsweise wenig zu meckern findet, gerade wenn man sie mit den diversen Mobile-Browsern vergleicht. Android-Standardbrowser und iOS-Safari sind was die fehlenden Features, zahllosen Bugs und Extravaganzen angeht nicht in einer Liga mit modernen Internet Explorern … jedenfalls quantitativ betrachtet. Was die „Qualität“ der einzelnen Probleme angeht fühlt man sich punktuell auch bei den IE mit zweistelliger Versionsnummer wie früher, zu glorreichen IE6-Zeiten …
Die Same-Origin-Policy (SOP) ist ein Sicherheitskonzept in Browsern, das den Zugriff auf Objekte wie z.B. Scripts nur dann erlaubt, wenn sie dem gleichen Speicherort entspringen. Als Ursprung bzw. Origin gilt die Kombination als Host, Protokoll und Port einer Web-Adresse; Ressourcen von http://foo.com kann http://www.bar.org nicht ohne weiteres verwenden – bei einem entsprechenden Versuch hagelt es Sicherheitsfehler (Details bei Wikipedia). In modernen Browsern lässt sich die SOP punktuell außer Kraft setzen um z.B. API-Verwendung über Domaingrenzen hinweg zu ermöglichen. Und manchmal sollten Browser die SOP auf eigene Faust ignorieren. Und hier fangen die IE-Probleme an.
Die File API von HTML5 erlaubt es, aus dem nichts Dateiressourcen zu erzeugen. Einfach mit new Blob(content, type) ein neues Blob-Objekt anlegen und fertig! Für sich genommen ist ein solches Objekt aber noch ziemlich nutzlos, denn ohne eine URL kann der Browser es außerhalb von JavaScript nicht verwenden. Mit Objekt-URLs lassen sich aber URLs auf beliebige JS-Objekte erzeugen, die dann benutzt werden können wie jede andere URL auch. Der folgende Schnipsel (hier auf jsFiddle) öffnet ein neues Fenster mit einer Textdatei, die es eigentlich gar nicht gibt; sie ist ein reines JavaScript-Konstrukt:
var textFile = new Blob(['Hallo Welt!'], {
type: 'text/plain'
});
var url = window.URL.createObjectURL(textFile);
window.open(url);
Auffällig an der URL des Popups ist, dass sie einen anderen Origin als die erzeugende Seite zu haben scheint; logisch, denn sie ist ja auch nicht der Seite selbst, sondern einem Script entsprungen:
Natürlich wissen alle Browser, dass sie eine solche URL behandeln sollten, als hätte sie den gleichen Origin wie die Seite, die die URL erzeugt hat … es sei denn der Browser ist IE 10 oder 11 und versucht etwas mit Web Workers zu machen. Erzeugt man einen JavaScript-Blob und steckt diesen in den Worker-Constructor hinein, so werfen IE 10 und 11 einen SecurityError. Dieser ist eigentlich für den Fall reserviert, dass die die Constructor-Script-URL von einem fremden Origin kommt – und nicht mal Microsofts eigene Dokumentation behauptet, dass das hier der Fall sein könnte. dieses kleine Testscript produziert in allen modernen Browsern außer den IE 10 und 11 (und natürlich dem Android-Browser, der keine Web Workers kennt) ein Alert, im IE 10 und 11 eine Security-Exception.
Interessant ist, dass Web Workers die einzige API zu sein scheint, bei der dem IE dieses SOP-Fehlurteil unterläuft. Ajax-Requests auf Blob-URLs stellen jedenfalls kein Problem dar. Ob da wohl jede API ihre eigene SOP-Implementierung hat? Man weiß es nicht, aber das ist auch egal. Man soll ja nicht nur rumjammern, sondern sein Schicksal selbst in die Hand nehmen – also auf zum IE-Bugtracker!
Dort stellt man fest, dass dieses Problem schon mehrfach gemeldet, aber auf verschiedenste Weisen von Microsoft und der Gesellschaft zu Förderung von Textbausteinen abgebügelt wurde (1,
2,
3). Aus irgendwelchen Gründen hat mein Bug Report zu dem Thema (wer mehr als eine SOP-Implementierung hat, kann auch mehrere identische Bugreports aushalten) eine Antwort aus Textbausteinen bekommen, die nicht komplett ablehnend sind. Dass es explizit verboten ist, Dateien mit der Erweiterung .html an Bugreports für einen Webbrowser anzuhängen lässt mich zwar ein bisschen an der Ersthaftigkeit der ganzen Bugtracker-Operation zweifeln. Und ein Fix vor dem IE 12 ist natürlich ausgeschlossen. Aber immerhin!
Was ich jetzt gern von euch hätte, wäre dass ihr euch jetzt alle einen Microsoft-Account klickt und mit aller Macht den „betrifft mich ebenfalls“-Link in meinem Bug Report malträtiert. Ich würde erstens gern diesen Bug gefixt sehen, zweitens würde mich stark interessieren, ob wirklich irgendwer bei MS den Bugtracker zur Kenntnis nimmt. Dann wäre es nämlich wirklich mal an der Zeit, mit den billigen IE-Witzen aufzuhören.
Vor langer Zeit schrieb ich einst eine Artikelserie über ECMAScript 5, den seit 2011 aktuellen JavaScript-Standard. Der Inhalt der einzelnen Artikel war notgedrungen in Teilen etwas verknappt – schließlich sollte das Ganze in 6 Blogposts passen. Zu erzählen wäre eigentlich viel viel mehr gewesen, denn es gibt allerlei Edge Cases und interessante Details. Außerdem sind heutzutage die Internet Explorer der Versionen 8 und älter die einzigen Browser, die den neuen Standard noch nicht unterstützen. Anders gesagt: in jedem aktuellen Browser und in jedem Vorgänger jedes aktuellen Browsers ist Unterstützung vorhanden und auf Plattformen wie NodeJS sowieso. Gleichzeitig hat mir eine Twitter-Umfrage gezeigt, dass viele Entwickler die meisten ES5-Features noch gar nicht auf dem Schirm haben (während gleichzeitig schon alle Welt von ECMAScript 6 redet). Es wird also Zeit, das Thema mal wieder auf die Agenda zu setzen, Zeit für das E-Book zu ECMAScript 5.
Das E-Book ist mehr als nur eine überarbeitete Fassung der Artikelserie. Zwar ist der Gegenstand der Texte notgedrungen der gleiche, doch so gut wie jeder Satz wurde überarbeitet und das E-Book enthält vieles, was in der Artikelserie einfach keinen Platz hatte. Während die Artikelserie in DIN A4 umgerechnet ca. 30 Seiten belegt, wiegt das E-Book mehr als 120 Seiten ‐ und diese Extra-Seiten enthalten alles andere als reines Füllmaterial! Beispiele für Neues:
Wirklich jedes neues ES5-Feature, selbst die in der Artikelsiere übergangenen neuen Date- und JSON-Funktionen
Ausführlichere und präzisere Erläuterungen zu allen Themen von der JavaScript-Geschichte bis hin zu den neuen APIs
Exotischere Konzepte wie z.B. bound function werden ausführlich erklärt
Es gibt ausführlichere und zahlreichere Codebeispiele zu jedem Aspekt von ES5
Praktische Anwendungsbeispiele für die tägliche Arbeit (z.B. mit jQuery)