JavaScript-Quiz

mag.js-Redaktion
Wie gut kennen Sie JavaScript? Die mag.js-Redaktion hat sich 25 Fragen wechselnden Schwierigkeitsgrads ausgedacht, mit denen Sie Ihre JavaScript-Kenntnisse überprüfen können. Und wenn Sie einmal etwas nicht wissen, so lernen Sie dazu, denn jede Frage kommt mit einer Erklärung der richtigen Antwort.

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 oder function).

Wie heißt der Erfinder von JavaScript?
Im Jahre 1995 implementierte Brendan Eich die erste Version von JavaScript für die Firma Netscape Communications, innerhalb von 10 Tagen.
Wie lautete der Mädchenname von JavaScript kurz nach ihrer Geburt?
Im Web-Browser „Netscape Navigator“ hieß JavaScript zuerst Mocha, dann LiveScript und schließlich JavaScript.
Welche der folgenden Sprachen hatte keinen Einfluss auf JS?
Leicht vereinfacht ausgedrückt: Von Self hat JavaScript die prototypische Vererbung, von Scheme die Closures und von Java und von Perl die Syntax. Prolog, eine Logik-Programmiersprache, hatte keinen Einfluss auf JavaScript.
Wer passt hier nicht in die Reihe?
Abgesehen von AppleScript, der Scriptingsprache für Mac OS, haben alle diese Sprachen etwas mit JavaScript zu tun: ActionScript (die Programmiersprache von Flash) basiert auf JavaScript, LiveScript war ein früherer Name von JavaScript und CoffeeScript wird nach JavaScript übersetzt (und hat zudem eine Semantik, die eng an JavaScript angelehnt ist).
Was ist false?
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.
Welcher der folgenden Werte ist nicht falsy?
„Falsy“ heißt soviel wie „wird 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
Was ist true?
Die einfache Gleichheit == 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.
Was gibt das folgende Script aus?
var a = 1; foo(); bar(); 
function foo() { a = 2 } 
var bar = function() { a = 3 }; 
console.log(window.a);
Hoisting ist das Phänomen, dass JavaScript Funktionsdeklarationen wie die für 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
Welche der folgenden Zuweisungen ist syntaktisch nicht korrekt?
Variablennamen müssen anfangen mit einem Buchstaben (inkl. griechischer Buchstaben!), $ 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.
Was ist die Ausgabe beim Aufruf von jane.describe()?
Im folgenden Code werden zwei ECMAScript-5-Features verwendet (welche?). Was wird ausgegeben, wenn man 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); 
        }); 
    }
};
Die zwei ECMAScript-5-Features sind die 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ür this 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 dann that.name.
Welcher der folgenden Ausdrücke ergibt true?
Wenn zwei Operanden mit strenger Gleichheit === (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.
Welchen Wert hat 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.
Was ist das Ergebnis von 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.
Was ist das Ergebnis von 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.
Welcher der folgenden Ausdrücke ist 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.
Was ist das Ergebnis von 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.
Welchen Wert hat b?
var a = {}, b = (new Object(a) === a);
Für 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.
Was ist das Ergebnis des folgenden Ausdrucks?
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

Mantisse × 2Exponent

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
Wie prüft man am sichersten, ob 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.
Was ist false?
Der Reihe nach:
  1. (+true, +false) ist dasselbe wie (Number(true), Number(false)), was dasselbe ist wie (1, 0). Dieser Ausdruck ergibt 0, 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 gleich true ist.
  2. JavaScript speichert eingegebene Dezimalbrüche im Binärformat. Deshalb können 110 und 15 nicht präzise dargestellt werden, denn Brüche müssen immer eine ganze Zahl sein, die durch eine Zweierpotenz dividiert wird. Vergleiche: 13 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 somit false.
  3. Der rechte Operand wird durch das Plus zu einer Zahl. Deshalb konvertiert < beide Operanden zu Zahlen, bevor es den Vergleich durchführt und false zurückgibt. Ein lexikografischer Vergleich von Strings fällt anders aus, da hier der Vergleich der ersten Zeichen ("9" < "1") dominiert.
    > "99" < "101"
    false
    
  4. Der Ausdruck ist äquivalent zu
    typeof "undefined" === "string"
    
    Also ist das Ergebnis true.
  5. isNaN wandelt sein Argument als erstes in eine Zahl um. Also ist der Ausdruck äquivalent zu
    isNaN(Number("abc"))
    
    Folglich ist das Ergebnis true.
Welche Schreibweise für eine IIFE (engl. immediately invoked function expression, ein direkt aufgerufener Funktionsausdruck) ist falsch?
In JavaScript wird sehr genau zwischen Anweisungen und Ausdrücken unterschieden. Eine Anweisung bewirkt direkt etwas. Ein Ausdruck wird zu einem Wert ausgewertet und kann innerhalb von Anweisungen eingesetzt werden, z.B., um einer Funktion einen Wert zu übergeben oder einer Variablen einen Wert zuzuweisen. Zudem kann man an jeder Stelle, wo eine Anweisung erwartet wird, auch einen Ausdruck hinschreiben (sogennante Ausdrucksanweisungen, engl. expression statements). Beispiele:
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.
Jedes Objekt fängt eine Kette von Prototypen mit 0 oder mehr Mitgliedern an. Welches Objekt ist fast immer das letzte Mitglied?
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
Welcher der folgenden Ausdrücke ist true?
Für den Operator < 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 und toString 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 ist false.
  • Antwort 2: Erneut werden zwei Objekte verglichen und müssen in primitive Werte umgewandelt werden. Der Ausdruck ist false, da er äquivalent ist zu
    10 < 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 damit false.
Welcher der folgenden Ausdrücke ist false?
Erklärungen:
  1. true. Einen Ausdruck (beliebig oft) zu klammern, ändert seinen Wert nicht.
  2. 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.
  3. 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 (per toString). Für den Vergleich "" == 0 wird "" in eine Zahl konvertiert. 0 == 0 ergibt schließlich true.
  4. Number.MIN_VALUE ist der kleinste Wert größer Null, den JavaScript noch präzise darstellen kann.
  5. NaN ist der einzige Wert, der nicht zu sich selbst gleich ist. Das wird als Bug gewertet. Um NaN zu finden, muss man die Funktion isNaN einsetzen.
    > var x = NaN;
    > x === NaN
    false
    > isNaN(x)
    true
    
Welches der folgenden Ergebnisse ist true?
Im Folgenden wird der Ausdruck Schritt für Schritt ausgewertet.
  1. ++[[]][+[]] + [+[]]
    In JavaScript konvertiert der Operator + Werte zu Zahlen, somit ist +[] gleich 0.
  2. ++[[]][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 als Number([]) + 1 (wobei wir damit das Rückspeichern ignorieren).
  3. (Number([])+1) + [0]
    Number([]) konvertiert das Argument in zwei Schritten zu einer Zahl: Zuerst wird [] in einen primitiven Wert konvertiert, durch eine der Methoden valueOf() oder toString(). [].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.
  4. 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.
  5. 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