Dieses Kapitel behandelt JavaScripts Anweisungen: Variablendeklarationen, Schleifen, bedingte Anweisungen und andere.
var wird verwendet, um eine Variable zu deklarieren, was die Variable erstellt und Ihnen ermöglicht, damit zu arbeiten. Das Zuweisungszeichen (=) wird verwendet, um ihr einen Wert zuzuweisen:
varfoo;foo='abc';
var erlaubt Ihnen auch, die beiden vorherigen Anweisungen zu einer einzigen zu kombinieren
varfoo='abc';
Schließlich können Sie auch mehrere var-Anweisungen zu einer zusammenfassen
varx,y=123,z;
Lesen Sie mehr darüber, wie Variablen funktionieren, in Kapitel 16.
Zusammengesetzte Anweisungen wie Schleifen und bedingte Anweisungen haben einen oder mehrere eingebettete „Körper“ – zum Beispiel die while-Schleife:
while(«condition»)«statement»
Für den Körper «statement» haben Sie eine Wahl. Sie können entweder eine einzelne Anweisung verwenden
while(x>=0)x--;
oder Sie können einen Block verwenden (der als einzelne Anweisung zählt):
while(x>0){x--;}
Sie müssen einen Block verwenden, wenn der Körper mehrere Anweisungen umfassen soll. Sofern die gesamte zusammengesetzte Anweisung nicht in einer einzigen Zeile geschrieben werden kann, empfehle ich die Verwendung eines Blocks.
Dieser Abschnitt untersucht die Schleifenanweisungen von JavaScript.
Die folgenden Mechanismen können mit allen Schleifen verwendet werden:
break ⟦«label»⟧
continue ⟦«label»⟧
Ein Label ist ein Bezeichner, gefolgt von einem Doppelpunkt. Vor einer Schleife ermöglicht ein Label das Beenden oder Fortsetzen dieser Schleife, selbst aus einer verschachtelten Schleife heraus. Vor einem Block können Sie aus diesem Block ausbrechen. In beiden Fällen wird der Name des Labels zu einem Argument von break oder continue. Hier ist ein Beispiel für das Ausbrechen aus einem Block:
functionfindEvenNumber(arr){loop:{// labelfor(vari=0;i<arr.length;i++){varelem=arr[i];if((elem%2)===0){console.log('Found: '+elem);breakloop;}}console.log('No even number found.');}console.log('DONE');}
Eine while-Schleife:
while(«condition»)«statement»
führt statement aus, solange condition wahr ist. Wenn condition immer true ist, erhalten Sie eine Endlosschleife
while(true){...}
Im folgenden Beispiel entfernen wir alle Elemente eines Arrays und protokollieren sie in der Konsole
vararr=['a','b','c'];while(arr.length>0){console.log(arr.shift());}
Hier ist die Ausgabe
a b c
Eine do-while-Schleife:
do«statement»while(«condition»);
führt statement mindestens einmal aus und dann, solange condition wahr ist. Zum Beispiel
varline;do{line=prompt('Enter a number:');}while(!/^[0-9]+$/.test(line));
In einer for-Schleife:
for(⟦«init»⟧;⟦«condition»⟧;⟦«post_iteration»⟧)«statement»
init wird einmal vor der Schleife ausgeführt, die fortgesetzt wird, solange condition true ist. Sie können var in init verwenden, um Variablen zu deklarieren, aber der Scope dieser Variablen ist immer die gesamte umgebende Funktion. post_iteration wird nach jeder Iteration der Schleife ausgeführt. Wenn man all dies berücksichtigt, ist die vorherige Schleife äquivalent zur folgenden while-Schleife
«init»;while(«condition»){«statement»«post_iteration»;}
Das folgende Beispiel ist der traditionelle Weg zur Iteration über Arrays (andere Möglichkeiten werden in Best Practices: Iterating over Arrays) beschrieben
vararr=['a','b','c'];for(vari=0;i<arr.length;i++){console.log(arr[i]);}
Eine for-Schleife wird endlos, wenn Sie alle Teile des Kopfes weglassen
for(;;){...}
Eine for-in-Schleife:
for(«variable»in«object»)«statement»
iteriert über alle Eigenschaftsschlüssel von object, einschließlich vererbter. Jedoch werden Eigenschaften, die als nicht aufzählbar markiert sind, ignoriert (siehe Property Attributes and Property Descriptors). Die folgenden Regeln gelten für for-in-Schleifen
var verwenden, um Variablen zu deklarieren, aber der Scope dieser Variablen ist immer die gesamte umgebende Funktion.Verwenden Sie for-in nicht, umüber Arrays zu iterieren. Erstens iteriert es über Indizes, nicht über Werte:
> var arr = [ 'a', 'b', 'c' ];
> for (var key in arr) { console.log(key); }
0
1
2Zweitens iteriert es auch über alle (nicht-Index-)Eigenschaftsschlüssel. Das folgende Beispiel verdeutlicht, was passiert, wenn Sie eine Eigenschaft foo zu einem Array hinzufügen
> var arr = [ 'a', 'b', 'c' ];
> arr.foo = true;
> for (var key in arr) { console.log(key); }
0
1
2
fooDaher sind Sie mit einer normalen for-Schleife oder der Array-Methode forEach() besser bedient (siehe Best Practices: Iterating over Arrays).
Die for-in-Schleife iteriert über alle (aufzählbaren) Eigenschaften, einschließlich vererbter. Das ist möglicherweise nicht das, was Sie wollen. Verwenden wir den folgenden Konstruktor, um das Problem zu veranschaulichen:
functionPerson(name){this.name=name;}Person.prototype.describe=function(){return'Name: '+this.name;};
Instanzen von Person erben die Eigenschaft describe von Person.prototype, was von for-in gesehen wird
varperson=newPerson('Jane');for(varkeyinperson){console.log(key);}
Hier ist die Ausgabe
name describe
Normalerweise ist der beste Weg, for-in zu verwenden, vererbte Eigenschaften über hasOwnProperty() zu überspringen
for(varkeyinperson){if(person.hasOwnProperty(key)){console.log(key);}}
Und hier ist die Ausgabe
name
Es gibt noch einen letzten Vorbehalt: person kann eine Eigenschaft hasOwnProperty haben, die die Überprüfung verhindern würde. Um sicherzugehen, müssen Sie direkt auf die generische Methode (siehe Generic Methods: Borrowing Methods from Prototypes) Object.prototype.hasOwnProperty verweisen
for(varkeyinperson){if(Object.prototype.hasOwnProperty.call(person,key)){console.log(key);}}
Es gibt andere, bequemere Mittel zur Iteration über Eigenschaftsschlüssel, die in Best Practices: Iterating over Own Properties beschrieben werden.
Diese Schleife existiert nur in Firefox. Verwenden Sie sie nicht.
Dieser Abschnitt behandelt die bedingten Anweisungen von JavaScript.
In einer if-then-else-Anweisung:
if(«condition»)«then_branch»⟦else«else_branch»⟧
then_branch und else_branch können entweder einzelne Anweisungen oder Blöcke von Anweisungen sein (siehe The Bodies of Loops and Conditionals).
Sie können mehrere if-Anweisungen verketten:
if(s1>s2){return1;}elseif(s1<s2){return-1;}else{return0;}
Beachten Sie, dass in der vorherigen Anweisung alle else-Zweige einzelne Anweisungen (if-Anweisungen) sind. Programmiersprachen, die nur Blöcke für else-Zweige zulassen, benötigen eine Art else-if-Zweig zum Verketten.
Der else-Zweigder folgenden Anweisung wird als hängend bezeichnet, da nicht klar ist, zu welcher der beiden if-Anweisungen er gehört:
if(«cond1»)if(«cond2»)«stmt1»else«stmt2»
Hier ist eine einfache Regel: verwenden Sie Klammern. Der vorherige Ausschnitt ist äquivalent zum folgenden Code (bei dem offensichtlich ist, zu wem das else gehört):
if(«cond1»){if(«cond2»){«stmt1»}else{«stmt2»}}
Eine switch-Anweisung:
switch(«expression»){case«label1_1»:case«label1_2»:...«statements1»⟦break;⟧case«label2_1»:case«label2_2»:...«statements2»⟦break;⟧...⟦default:«statements_default»⟦break;⟧⟧}
wertet expression aus und springt dann zur case-Klausel, deren Label dem Ergebnis entspricht. Wenn kein Label übereinstimmt, springt switch zur default-Klausel, falls vorhanden, oder tut nichts sonst.
Der „Operand“ nach case kann jeder Ausdruck sein; er wird über === mit dem Parameter von switch verglichen.
Wenn Sie eine Klausel nicht mit einer beendenden Anweisung abschließen, wird die Ausführung in die nächste Klausel fortgesetzt. Die am häufigsten verwendete beendende Anweisung ist break. Aber return und throw funktionieren ebenfalls, auch wenn sie normalerweise mehr als nur die switch-Anweisung verlassen.
Das folgende Beispiel verdeutlicht, dass Sie break nicht verwenden müssen, wenn Sie throw oder return verwenden
functiondivide(dividend,divisor){switch(divisor){case0:throw'Division by zero';default:returndividend/divisor;}}
In diesem Beispiel gibt es keine default-Klausel. Daher geschieht nichts, wenn fruit keinem der case-Labels entspricht
functionuseFruit(fruit){switch(fruit){case'apple':makeCider();break;case'grape':makeWine();break;// neither apple nor grape: do nothing}}
Hier gibt es mehrere case-Labels hintereinander
functioncategorizeColor(color){varresult;switch(color){case'red':case'yellow':case'blue':result='Primary color: '+color;break;case'orange':case'green':case'violet':result='Secondary color: '+color;break;case'black':case'white':result='Not a color';break;default:throw'Illegal argument: '+color;}console.log(result);}
Dieses Beispiel zeigt, dass der Wert nach case ein beliebiger Ausdruck sein kann
functioncompare(x,y){switch(true){casex<y:return-1;casex===y:return0;default:return1;}}
Die vorherige switch-Anweisung sucht nach einer Übereinstimmung für ihren Parameter true, indem sie die case-Klauseln durchläuft. Wenn einer der case-Ausdrücke zu true ausgewertet wird, wird der entsprechende case-Körper ausgeführt. Daher ist der vorherige Code äquivalent zur folgenden if-Anweisung
functioncompare(x,y){if(x<y){return-1;}elseif(x===y){return0;}else{return1;}}
Normalerweise sollten Sie die letztere Lösung bevorzugen; sie ist selbsterklärender.
Dieser Abschnitt erklärt, wie die with-Anweisung in JavaScript funktioniert und warum ihre Verwendung abgeraten wird.
Die Syntax der with-Anweisung ist wie folgt
with(«object»)«statement»
Sie verwandelt die Eigenschaften von object in lokale Variablen für statement. Zum Beispiel
varobj={first:'John'};with(obj){console.log('Hello '+first);// Hello John}
Ihr beabsichtigter Verwendungszweck ist es, Redundanz zu vermeiden, wenn mehrmals auf ein Objekt zugegriffen wird. Das Folgende ist ein Beispiel für Code mit Redundanzen
foo.bar.baz.bla=123;foo.bar.baz.yadda='abc';
with macht dies kürzer
with(foo.bar.baz){bla=123;yadda='abc';}
Die Verwendung der with-Anweisung wird generell abgeraten (der nächste Abschnitt erklärt warum). Sie ist zum Beispiel im Strict Mode verboten:
> function foo() { 'use strict'; with ({}); }
SyntaxError: strict mode code may not contain 'with' statementsVermeiden Sie Code wie diesen:
// Don't do this:with(foo.bar.baz){console.log('Hello '+first+' '+last);}
Verwenden Sie stattdessen eine temporäre Variable mit einem kurzen Namen
varb=foo.bar.baz;console.log('Hello '+b.first+' '+b.last);
Wenn Sie die temporäre Variable b nicht dem aktuellen Scope aussetzen möchten, können Sie eine IIFE (siehe Introducing a New Scope via an IIFE) verwenden
(function(){varb=foo.bar.baz;console.log('Hello '+b.first+' '+b.last);}());
Sie haben auch die Möglichkeit, das Objekt, auf das Sie zugreifen möchten, zu einem Parameter der IIFE zu machen
(function(b){console.log('Hello '+b.first+' '+b.last);}(foo.bar.baz));
Um zu verstehen, warum with veraltet ist, schauen Sie sich das folgende Beispiel an und beachten Sie, wie das Argument der Funktion die Funktionsweise komplett verändert:
functionlogMessage(msg,opts){with(opts){console.log('msg: '+msg);// (1)}}
Wenn opts eine Eigenschaft msg hat, dann greift die Anweisung in Zeile (1) nicht mehr auf den Parameter msg zu. Sie greift auf die Eigenschaft zu
> logMessage('hello', {}) // parameter msg
msg: hello
> logMessage('hello', { msg: 'world' }) // property opts.msg
msg: worldEs gibt drei Probleme, die die with-Anweisung verursacht
Sie können nicht feststellen, worauf sich ein Bezeichner bezieht, indem Sie seine syntaktischen Umgebung (seinen lexikalischen Kontext) betrachten. Laut Brendan Eich war dies der eigentliche Grund, warum with veraltet wurde, und nicht Leistungserwägungen
withverletzt den lexikalischen Scope und macht die Programmanalyse (z. B. für Sicherheit) schwierig bis unmöglich.
with-Anweisung können Sie nicht statisch bestimmen, ob ein Name sich auf eine Variable oder eine Eigenschaft bezieht. Nur Variablen können von Minifiern umbenannt werden.Hier ist ein Beispiel dafür, wie with Code anfällig macht
functionfoo(someArray){varvalues=...;// (1)with(someArray){values.someMethod(...);// (2)...}}foo(myData);// (3)
Sie können den Funktionsaufruf in Zeile (3) verhindern, selbst wenn Sie keinen Zugriff auf das Array myData haben.
Wie? Indem Sie eine Eigenschaft values zu Array.prototype hinzufügen. Zum Beispiel
Array.prototype.values=function(){...};
Jetzt ruft der Code in Zeile (2) someArray.values.someMethod() anstelle von values.someMethod() auf. Der Grund ist, dass values innerhalb der with-Anweisung jetzt someArray.values und nicht mehr die lokale Variable aus Zeile (1) bezeichnet.
Dies ist kein reines Gedankenexperiment: die Array-Methode values() wurde zu Firefox hinzugefügt und brach das TYPO3-Content-Management-System. Brandon Benvie fand heraus, was schiefgelaufen war.
Die Syntax für die debugger-Anweisung ist wie folgt:
debugger;
Wenn ein Debugger aktiv ist, fungiert diese Anweisung als Breakpoint; wenn nicht, hat sie keine beobachtbare Auswirkung.