JavaScript-Quiz
Hinweise:
- Richtig ist immer genau eine Antwort.
- Wir gehen vom ECMAScript-3-Standard aus.
- Die Code-Beispiele laufen immer im globalen Kontext (nicht im Kontext von
evaloderfunction).
Number.MIN_VALUE * 2 === -Infinity, denn Number.MIN_VALUE
ist die kleinste Fließkommazahl größer 0, die sich noch darstellen lässt.
Sie hat nichts mit minus Unendlich zu tun.
false, wenn man es zu Boolean konvertiert“.
Die Falsy-Werte sind undefined, null, -0,
+0, NaN, "" und
natürlich false. Ein leeres Object {} hingegen ist „truthy“.
Die Probe aufs Exempel lässt sich mit der Funktion Boolean() machen,
die ihr Argument zu Boolean konvertiert. Beispiele:
> Boolean({})
true
> Boolean("")
false
true?== ist so definiert, dass undefined und null zwar zueinander gleich sind, aber zu sonst keinem anderen Wert. Bei den Operatoren >= und > hingegen, werden die Operanden erst zu primitiven Werten konvertiert. Sie sind dann entweder beide Strings und werden als solche verglichen. Oder sie werden zu Number konvertiert und dementsprechend verglichen. Folglich ist nur 0 >= null wahr, denn null wird vor dem Vergleich zu 0 konvertiert (undefined konvertiert zu Number ergibt NaN). Wenn Ihnen jetzt die Regeln für == zu kompliziert erscheinen, ist das kein Problem, denn es sollte sowieso nie verwendet werden, die strenge Gleichheit === ist immer die bessere Wahl.
var a = 1; foo(); bar();
function foo() { a = 2 }
var bar = function() { a = 3 };
console.log(window.a);
foo ganz an den Anfang eines Bereiches schiebt.
Somit ist foo definiert, wenn es am Anfang aufgerufen wird.
bar wird durch eine Zuweisung definiert und ist somit in
der ersten Zeile noch nicht bekannt. Es gibt also einen Fehler, beispielsweise:
TypeError: undefined is not a function
$ oder _. Bei !!0 wird der Präfix-Operator
! (boolesche Negation) zweimal auf die Zahl 0 angewendet, mit dem Ergebnis false, nach
Typkonversion. Fehlerhaft ist hingegen &_0, denn &
ist ein zweistelliger Operator.
jane.describe()?jane.describe() aufruft?
var name = "GLOBAL";
var jane = {
name: "Jane",
friends: [ "Tarzan" ],
describe: function() {
"use strict"; // (*)
this.friends.forEach(function(friend) { // (**)
console.log(this.name+" kennt "+friend);
});
}
};
forEach-Methode für Arrays (**) und der Strict Mode (*), mit dem ein paar Dinge strenger gehandhabt und genauer überprüft werden. Der Strict Mode beeinflusst die Ausgabe. Ohne ihn wäre sie „GLOBAL kennt Tarzan“. Mit Strict Mode gibt es eine Fehlermeldung, weil this dann undefined ist. Es gibt zwei Möglichkeiten, den Fehler in Methode describe() zu beheben:
- Als zweites Argument von
forEach()kann ein Wert fürthisangegeben werden:this.friends.forEach(function(friend) { ... }, this); - Man kann am Anfang von describe() eine Variable
thateinführen:var that = this;
Für die Ausgabe verwendet man dannthat.name.
true?=== (engl. strict equality) verglichen werden, müssen sie entweder beide derselbe primitive Wert (undefined, null, ein Wahrheitswert, eine Zahl, eine Zeichenkette) sein oder dasselbe Objekt. Da {} jedes Mal ein neues Objekt erzeugt, kann {} === {} nicht gelten. Dann bleiben noch Antworten 3 und 4, wo zwei gleiche primitive Werte verglichen werden. NaN ist der einzige JavaScript-Wert, der nicht zu sich selbst streng gleich ist. Das wird allgemein als Bug eingeschätzt. Also bleibt nur noch Antwort 3, wo beide Operanden (nach Auswertung) die Zeichenkette "30" sind. Dass 3+"0" zu "30" wird, kommt daher, dass der Plus-Operator beide Operanden zu Strings konvertiert und aneinander hängt, sobald einer von ihnen ein String ist.
x, nachdem folgender Code ausgeführt wurde?
var x = {}; Array.prototype.push.call(x, "foo");
Array.prototype.push ist eine sogenannte generische Methode (engl. generic method). Sie ist zwar Teil des Array-Typs, kann aber auf Instanzen anderer Typen angewendet werden, solange diese Lese- und Schreibzugriffe per Index erlauben und ein Property length haben – existiert dieses Property nicht, wird der Wert 0 verwendet.
Die call-Methode schiebt push die Fremd-Instanz x unter: Das erste Argument sorgt dafür, dass this innerhalb von push dessen Wert hat. Folglich wird das Objekt x wie ein Array behandelt und entsprechend geändert. Die Länge erhöht sich, am Ende kommt ein neues Element hinzu. Das Ergebnis ist Antwort 5. Der Rückgabewert von push ist die neue Länge von x, 1. Andere generische Array-Methoden erstellen ein neues Array und geben das zurück, aber nicht push.
Object.getOwnPropertyNames([ "foo" ])?Object.getOwnPropertyNames(obj) gibt die Namen aller eigenen (engl. own) Propertys eines Objektes obj zurück. Für Arrays sind das alle Indizes, length und die Namen aller sonstigen Propertys, die dem Array hinzugefügt wurden. Ein evtl. unerwartetes Phänomen ist, dass Array-Elemente in Propertys gespeichert werden, deren Namen Strings sind (nämlich die zu String konvertierten Indices der Elemente). In der letzten Antwort werden alle Propertys eines Arrays aufgezählt, inklusive der von den Prototypen Array.prototype und Object.prototype geerbten.
Object.keys([ "foo" ])?Object.keys(obj) funktioniert ähnlich zu Object.getOwnPropertyNames(obj) – es gibt ein Array mit den Namen der eigenen Propertys von obj zurück. Dabei werden aber nur Propertys berücksichtigt, die aufzählbar (engl. enumerable) sind. Aufzählbarkeit ist eine Eigenschaft von Propertys, mit der sie als besonders wichtig für manche Operationen gekennzeichnet werden. So sind beispielsweise in Arrays nur die Indizes aufzählbar (und ggf. andere Propertys, die man selbst hinzufügt). Eine andere Sichtweise ist, dass das Array-Property length vor Object.keys() versteckt wird, indem es „non-enumerable“ gemacht wird („enumerable“ ist Standard).
Enthält ein Array ein Loch, wird der entsprechende Index nicht angegeben:
> Object.keys(["a",, "b"]) [ '0', '2' ]Die Methode
Object.defineProperty() gibt Ihnen die Möglichkeit, selbst Propertys zu erstellen, die non-enumerable sind.
true?indexOf sucht Werte in einem Array. Wird der Wert gefunden, so ist das Ergebnis der Index des Elements. Wird er nicht gefunden, so wird -1 zurückgegeben. Folglich läuft die Frage darauf hinaus, in welchem der erzeugten Arrays der Wert 2 enthalten ist. Antworten 1 und 3 scheiden damit offensichtlich aus. In Antwort zwei wird ein leeres Array der Länge 2 erzeugt und nicht, wie man erwarten würde, ein Array mit dem einzigen Element 2. Das ist eine bekannte Eigenart von JavaScript, ein Bug. Deshalb sollte man nie den Array-Konstruktor verwenden, um Arrays mit Werten zu füllen, Array-Literale sind hierzu viel besser geeignet: Für Antwort 1 würde man also besser [3,4,5] verwenden. In Antwort 5 ändert die Methode push das Array, auf das sie angewendet wird, und gibt die neue Länge 1 zurück. Also wird indexOf für 1 aufgerufen – ein Fehler, da Zahlen diese Methode nicht haben. Antwort 4 ist folglich richtig. slice ist eine generische Methode und wandelt das Array-ähnliche this-Argument von call in das Array [ 2 ] um.
typeof new String(123)?
String ist ein sogenannter Wrapper-Typ; seine Instanzen sind Objekte, die Zeichenketten (also primitive Werte) verpacken. String kann als Konstruktor aufgerufen werden und erzeugt dann Objekte:
> typeof new String(123) 'object'Wird er hingegen als Funktion aufgerufen, so konvertiert er das Argument zu einer Zeichenkette:
> typeof String(123) 'string'Wrapper-Objekte werden in JavaScript so gut wie nie eingesetzt, da man mit primitiven Werten fast alles machen kann.
b?var a = {}, b = (new Object(a) === a);
Object ist es egal, ob es als Konstruktor (per new) oder als Funktion aufgerufen wird. In beiden Fällen wird das Argument in ein Objekt umgewandelt. Ist das Argument bereits ein Objekt, so wird es unverändert zurückgegeben. Bei der strengen Gleichheit ist ein Objekt nur zu sich selbst gleich. Diese Bedingung ist hier erfüllt, also ist das Ergebnis true.
9007199254740992 + 1
9007199254740992 ist die größte in JavaScript darstellbare ganze Zahl, so dass alle ganzen Zahlen, die kleiner sind, auch noch dargestellt werden können. Warum gerade diese Zahl? Zahlen in JavaScript basieren auf dem IEEE-754-Standard und werden mit 64 bit Genauigkeit (doppelte Genauigkeit) abgespeichert. Von den 64 bit werden 53 bit für die Ziffernstellen (Mantisse) verwendet. Somit ist die größte Mantisse, die verwendet werden kann, 253−1 (entspricht 9007199254740991). Separat kommt ein Exponent hinzu zwischen −1022 und +1023. Eine ganze Zahl ist in JavaScript immer
Daher sprengt 9007199254740992 die Mantisse nicht, denn es wird intern gespeichert als 1 × 253 (die Mantisse ist also 1). 9007199254740992 um 1 erhöhen klappt hingegen nicht mehr, da dann die Präzision der Mantisse nicht reicht. 9007199254740992 mit einer ganzen Zahl zu multiplizieren ist meistens kein Problem. Für 10 bekommt man zum Beispiel das darstellbare Ergebnis 10 × 253. Außerdem kann man 9007199254740991 mit einer Zweierpotenz multiplizieren, weil das die Mantisse nicht „belastet“. Beispiele:
> 9007199254740992 + 1 9007199254740992 > 9007199254740993 9007199254740992 > 9007199254740992 * 10 90071992547409920 > 9007199254740992 * 4 36028797018963970
maybeArray ein Array ist?typeof klassifiziert Arrays immer als "object". Vergleiche mit dem Wert Array können scheitern, wenn maybeArray aus einem anderen Fenster oder einem anderen Frame kommt, da diese eigene Versionen von Array haben. Also fallen Lösungen 2 und 3 aus. Lösung 5 überprüft, ob maybeArray eine Methode sort hat, was für Anwendungen genügt, die nur diese Methode benötigen. Es ist aber kein guter Test, ob maybeArray ein Array ist. Also ist Lösung 4 korrekt. Hier wird die Methode Object.prototype.toString generisch verwendet. Diese Ur-Version von toString wird in den meisten Untertypen von Object überschrieben. Durch call haben wir ihre Funktionalität dennoch zur Verfügung: sie gibt für Objekte den Wert des internen [[Class]]-Propertys aus (das den Namen des Typs enthält), umrahmt von eckigen Klammern und object. Beispiele:
> Object.prototype.toString.call({})
'[object Object]'
> Object.prototype.toString.call([])
'[object Array]'
> Object.prototype.toString.call(/abc/)
'[object RegExp]'
Durch diesen Trick sind wir unabhängig vom exakten Wert von Array, da der Rückgabewert von toString immer gleich sein wird, egal aus welchem Fenster oder Frame maybeArray kommt.
false?(+true, +false)ist dasselbe wie(Number(true), Number(false)), was dasselbe ist wie(1, 0). Dieser Ausdruck ergibt0, da der Komma-Operator immer beide Operanden ausrechnet und das Ergebnis der rechten Seite zurückgibt. Also ist der Ausdruck aus der Frage gleich[true, false][0], was gleichtrueist.- JavaScript speichert eingegebene Dezimalbrüche im Binärformat. Deshalb können und nicht präzise dargestellt werden, denn Brüche müssen immer eine ganze Zahl sein, die durch eine Zweierpotenz dividiert wird. Vergleiche: lässt sich nicht präzise als Dezimalzahl darstellen. Durch die Addition wird die mangelnde Präzision offensichtlich:
> 0.1 + 0.2 0.30000000000000004
Der Vergleich ergibt somitfalse. - Der rechte Operand wird durch das Plus zu einer Zahl. Deshalb konvertiert
<beide Operanden zu Zahlen, bevor es den Vergleich durchführt undfalsezurückgibt. Ein lexikografischer Vergleich von Strings fällt anders aus, da hier der Vergleich der ersten Zeichen ("9" < "1") dominiert.> "99" < "101" false
- Der Ausdruck ist äquivalent zu
typeof "undefined" === "string"
Also ist das Ergebnistrue. isNaNwandelt sein Argument als erstes in eine Zahl um. Also ist der Ausdruck äquivalent zuisNaN(Number("abc"))Folglich ist das Ergebnistrue.
var x = 3; // Anweisung: weise x den Ausdruck 3 zu foo(); // expression statement (foo() ist ein Ausdruck)Trickreich wird es bei Funktionen, weil es sowohl die Funktionsdeklarationen (Anweisungen) gibt:
function foo(x, y) {
...
}
Als auch Funktionsausdrücke (Ausdrücke):
var foo = function (x, y) {
...
};
Beginnt eine Anweisung mit dem Schlüsselwort function, so geht JavaScript davon aus, dass eine Funktionsdeklaration erfolgt. Aus diesem Grund ist Antwort 4 falsch: Erstens muss eine Funktionsdeklaration einen Namen haben. Zweitens kann sie nicht direkt aufgerufen werden. Daher bleibt einem nur, die Erwartungen von JavaScript zu ändern: Schreibt man etwas in Klammern, wird ein Ausdruck erwartet. Dieses Verfahren wird in Antwort 1-3 (korrekt) eingesetzt. Antwort 5 funktioniert, da nach einer Zuweisung ebenfalls ein Ausdruck erwartet wird. Hier braucht man also keine Klammern.
null bildet immer den Abschluss einer Prototypen-Kette, ist aber kein
Objekt. Also ist die richtige Antwort Object.prototype. Mit Tricks kann
man diesem Objekt entkommen, aber normalerweise ist es immer mit an Bord:
> Object.prototype.isPrototypeOf({})
true
> Object.prototype.isPrototypeOf([])
true
> Object.prototype.isPrototypeOf(function() {})
true
> Object.prototype.isPrototypeOf(new Date())
true
true?< werden zuerst die Operanden zu primitiven Werten umgewandelt. Sind beide Ergebnisse Strings, wird lexikografisch verglichen, ansonsten werden die Werte zu Zahlen umgewandelt und numerisch verglichen. Damit ist Antwort 3 korrekt. Numerisch verglichen wäre 100 nicht kleiner als 9, aber da lexikografisch der Vergleich der ersten Zeichen ("1" < "9") dominiert, ist der Ausdruck wahr. Erklärungen der verbleibenden Antworten:
- Antwort 1: Um ein Objekt zu einem primitiven Wert zu konvertieren, werden die Methoden
valueOfundtoStringaufgerufen. Das erste Ergebnis, das primitiv ist, wird verwendet.valueOfgibt für{}das Objekt selbst zurück, fällt also aus.toStringgibt hingegen eine Zeichenkette zurück, also einen primitiven Wert. Damit ist der angegebene Vergleich äquivalent zu dem folgenden Ausdruck.'[object Object]' < '[object Object]'
Das istfalse. - Antwort 2: Erneut werden zwei Objekte verglichen und müssen in primitive Werte umgewandelt werden. Der Ausdruck ist
false, da er äquivalent ist zu10 < 6
- Antwort 4: Die folgenden Ausdrücke sind alle äquivalent.
true < false Number(true) < Number(false) 1 < 0 false
- Antwort 5: In JavaScripts lexikografischer Ordnung kommen die Großbuchstaben vor den Kleinbuchstaben, also ist z.B.
"B" < "a". Die Antwort ist damitfalse.
false?true. Einen Ausdruck (beliebig oft) zu klammern, ändert seinen Wert nicht.true.windowist das Objekt, das alle globalen Variablen als Propertys enthält. Es ist aber zudem selbt eine globale Variable. Also zeigt es auf sich selbst.true. Zuerst wird der rechte Operand ausgerechnet.![] Number([]) // Number macht Argument primitiv Number("") 0Damit lautet der zu berechnende Vergleich[] == 0. Zuerst wird[]in den primitiven Wert""konvertiert (pertoString). Für den Vergleich"" == 0wird""in eine Zahl konvertiert.0 == 0ergibt schließlichtrue.Number.MIN_VALUEist der kleinste Wert größer Null, den JavaScript noch präzise darstellen kann.NaNist der einzige Wert, der nicht zu sich selbst gleich ist. Das wird als Bug gewertet. UmNaNzu finden, muss man die FunktionisNaNeinsetzen.> var x = NaN; > x === NaN false > isNaN(x) true
true?++[[]][+[]] + [+[]]
In JavaScript konvertiert der Operator+Werte zu Zahlen, somit ist+[]gleich0.++[[]][0] + [0]
Der Ausdruck[[]][0]greift auf das erste Element von[[]]zu, nämlich[]. Mit++wird dieses Array-Element in eine Zahl konvertiert, erhöht, zurückgespeichert und als Ergebnis zurückgegeben. Wie das funktioniert, sieht man am besten an einem einfacheren Beispiel.> var x = ["3"]; > ++x[0] 4 > x [ 4 ]
Folglich lässt sich der Ausdruck auch schreiben alsNumber([]) + 1(wobei wir damit das Rückspeichern ignorieren).(Number([])+1) + [0]
Number([])konvertiert das Argument in zwei Schritten zu einer Zahl: Zuerst wird[]in einen primitiven Wert konvertiert, durch eine der MethodenvalueOf()odertoString().[].valueOf()liefert ein Objekt zurück, ist also ungeeignet. Folglich wird[].toString()aufgerufen. Der primitive Rückgabewert""wird dann in eine Zahl umgewandelt, nämlich in 0.1 + [0]
Der Plus-Operator wandelt zuerst beide Operanden in primitive Werte um. Ähnlich wie im vorhergehenden Schritt führt dies dazu, dass[0]zu"0"(dem Ergebnis von[0].toString()) wird.1ist bereits primitiv, muss also nicht konvertiert werden.1 + "0"
Sobald einer der Operanden ein String ist, wandelt Plus alle Operanden in Strings um und liefert die Konkatenation beider Werte zurück. Wir bekommen also das Ergebnis"10".
Zurücksetzen | Alle Lösungen einblenden | Alle Lösungen ausblenden