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
eval
oderfunction
).
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ürthis
angegeben werden:this.friends.forEach(function(friend) { ... }, this);
- Man kann am Anfang von describe() eine Variable
that
einfü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 gleichtrue
ist.- 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 undfalse
zurü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
. isNaN
wandelt 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
valueOf
undtoString
aufgerufen. Das erste Ergebnis, das primitiv ist, wird verwendet.valueOf
gibt für{}
das Objekt selbst zurück, fällt also aus.toString
gibt 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
.window
ist 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("") 0
Damit lautet der zu berechnende Vergleich[] == 0
. Zuerst wird[]
in den primitiven Wert""
konvertiert (pertoString
). Für den Vergleich"" == 0
wird""
in eine Zahl konvertiert.0 == 0
ergibt schließlichtrue
.Number.MIN_VALUE
ist der kleinste Wert größer Null, den JavaScript noch präzise darstellen kann.NaN
ist der einzige Wert, der nicht zu sich selbst gleich ist. Das wird als Bug gewertet. UmNaN
zu finden, muss man die FunktionisNaN
einsetzen.> 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.1
ist 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