Dieses Kapitel befasst sich mit „Grundlegendem JavaScript“, einem Namen, den ich für eine möglichst knappe Teilmenge von JavaScript gewählt habe, die Ihnen dennoch produktives Arbeiten ermöglicht. Wenn Sie anfangen, JavaScript zu lernen, empfehle ich Ihnen, eine Weile damit zu programmieren, bevor Sie sich mit dem Rest der Sprache befassen. So müssen Sie nicht alles auf einmal lernen, was verwirrend sein kann.
Dieser Abschnitt gibt einen kurzen Hintergrund zu JavaScript, um Ihnen zu helfen zu verstehen, warum es so ist, wie es ist.
ECMAScript ist der offizielle Name für JavaScript. Ein neuer Name wurde notwendig, da es eine Marke für JavaScript gibt (ursprünglich von Sun, jetzt von Oracle gehalten). Derzeit ist Mozilla eines der wenigen Unternehmen, denen es gestattet ist, den Namen JavaScript offiziell zu verwenden, da es vor langer Zeit eine Lizenz erhalten hat. Für den allgemeinen Sprachgebrauch gelten folgende Regeln:
JavaScript-Schöpfer Brendan Eich hatte keine andere Wahl, als die Sprache sehr schnell zu entwickeln (oder andere, schlechtere Technologien wären von Netscape übernommen worden). Er borgte sich von mehreren Programmiersprachen: Java (Syntax, primitive Werte versus Objekte), Scheme und AWK (First-Class-Funktionen), Self (prototypische Vererbung) und Perl und Python (Strings, Arrays und reguläre Ausdrücke).
JavaScript hatte bis ECMAScript 3 keine Ausnahmebehandlung. Dies erklärt, warum die Sprache Werte so oft automatisch konvertiert und so oft stillschweigend fehlschlägt: Sie konnte anfangs keine Ausnahmen auslösen.
Einerseits hat JavaScript Eigenheiten und es fehlt ihm einiges an Funktionalität (blockbezogene Variablen, Module, Unterstützung für Unterklassen usw.). Andererseits verfügt es über mehrere leistungsstarke Funktionen, mit denen Sie diese Probleme umgehen können. In anderen Sprachen lernen Sie Sprachfunktionen. In JavaScript lernen Sie oft stattdessen Muster.
Angesichts seiner Einflüsse ist es keine Überraschung, dass JavaScript einen Programmierstil ermöglicht, der eine Mischung aus funktionaler Programmierung (Higher-Order-Funktionen; integrierte map, reduce usw.) und objektorientierter Programmierung (Objekte, Vererbung) ist.
Dieser Abschnitt erklärt grundlegende syntaktische Prinzipien von JavaScript.
Einige Beispiele für Syntax:
// Two slashes start single-line commentsvarx;// declaring a variablex=3+y;// assigning a value to the variable `x`foo(x,y);// calling function `foo` with parameters `x` and `y`obj.bar(3);// calling method `bar` of object `obj`// A conditional statementif(x===0){// Is `x` equal to zero?x=123;}// Defining function `baz` with parameters `a` and `b`functionbaz(a,b){returna+b;}
Beachten Sie die beiden unterschiedlichen Verwendungen des Gleichheitszeichens:
=) wird verwendet, um einer Variablen einen Wert zuzuweisen.===) wird verwendet, um zwei Werte zu vergleichen (siehe Gleichheitsoperatoren).Um die Syntax von JavaScript zu verstehen, sollten Sie wissen, dass sie zwei Hauptsynthesekategorien hat: Anweisungen und Ausdrücke:
Anweisungen „tun Dinge“. Ein Programm ist eine Sequenz von Anweisungen. Hier ist ein Beispiel für eine Anweisung, die eine Variable foo deklariert (erstellt)
varfoo;
Ausdrücke erzeugen Werte. Sie sind Funktionsargumente, die rechte Seite einer Zuweisung usw. Hier ist ein Beispiel für einen Ausdruck
3*7
Der Unterschied zwischen Anweisungen und Ausdrücken wird am besten dadurch verdeutlicht, dass JavaScript zwei verschiedene Möglichkeiten hat, if-then-else zu tun – entweder als Anweisung:
varx;if(y>=0){x=y;}else{x=-y;}
oder als Ausdruck
varx=y>=0?y:-y;
Letzteres können Sie als Funktionsargument verwenden (aber nicht ersteres)
myFunction(y>=0?y:-y)
Schließlich können Sie überall dort, wo JavaScript eine Anweisung erwartet, auch einen Ausdruck verwenden; zum Beispiel
foo(7,1);
Die gesamte Zeile ist eine Anweisung (eine sogenannte Ausdrucksanweisung), aber der Funktionsaufruf foo(7, 1) ist ein Ausdruck.
Semikolons sind in JavaScript optional. Ich empfehle jedoch, sie immer einzufügen, da JavaScript andernfalls falsch über das Ende einer Anweisung raten kann. Die Details werden in Automatischer Semikolon-Einfügung erläutert.
Semikolons beenden Anweisungen, aber keine Blöcke. Es gibt einen Fall, in dem Sie ein Semikolon nach einem Block sehen werden: Ein Funktionsausdruck ist ein Ausdruck, der mit einem Block endet. Wenn ein solcher Ausdruck zuletzt in einer Anweisung kommt, folgt ihm ein Semikolon:
// Pattern: var _ = ___;varx=3*7;varf=function(){};// function expr. inside var decl.
JavaScript hat zwei Arten von Kommentaren: einzeilige Kommentare und mehrzeilige Kommentare. Einzeilige Kommentare beginnen mit // und enden am Zeilenende:
x++;// single-line comment
Mehrzeilige Kommentare werden durch /* und */ abgegrenzt:
/* This isa multilinecomment.*/
Variablen werden in JavaScript deklariert, bevor sie verwendet werden:
varfoo;// declare variable `foo`
Sie können eine Variable deklarieren und ihr gleichzeitig einen Wert zuweisen:
varfoo=6;
Sie können auch einer vorhandenen Variablen einen Wert zuweisen
foo=4;// change variable `foo`
Es gibt zusammengesetzte Zuweisungsoperatoren wie +=. Die folgenden beiden Zuweisungen sind äquivalent:
x+=1;x=x+1;
Bezeichner sind Namen, die in JavaScript verschiedene syntaktische Rollen spielen. Zum Beispiel ist der Name einer Variablen ein Bezeichner. Bezeichner sind Groß- und Kleinschreibungsempfindlich.
Ungefähr kann das erste Zeichen eines Bezeichners ein beliebiges Unicode-Buchstabe, ein Dollarzeichen ($) oder ein Unterstrich (_) sein. Nachfolgende Zeichen können zusätzlich jede Unicode-Ziffer sein. Daher sind die folgenden alle gültige Bezeichner
arg0_tmp$elemπ
Die folgenden Bezeichner sind reservierte Wörter – sie sind Teil der Syntax und können nicht als Variablenamen (einschließlich Funktionsnamen und Parameternamen) verwendet werden:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Die folgenden drei Bezeichner sind keine reservierten Wörter, aber Sie sollten sie so behandeln, als wären sie es
|
|
|
Schließlich sollten Sie auch die Namen von Standard-Globalvariablen meiden (siehe Kapitel 23). Sie können sie für lokale Variablen verwenden, ohne etwas kaputt zu machen, aber Ihr Code wird trotzdem verwirrend.
JavaScript hat viele Werte, die wir von Programmiersprachen erwarten: Booleans, Zahlen, Zeichenketten, Arrays und so weiter. Alle Werte in JavaScript haben Eigenschaften.[1] Jede Eigenschaft hat einen Schlüssel (oder Namen) und einen Wert. Sie können sich Eigenschaften wie Felder eines Datensatzes vorstellen. Sie verwenden den Punktoperator (.), um eine Eigenschaft zu lesen
value.propKey
Zum Beispiel hat der String 'abc' die Eigenschaft length
> var str = 'abc'; > str.length 3
Das Vorherige kann auch geschrieben werden als
> 'abc'.length 3
Der Punktoperator wird auch verwendet, um einen Wert einer Eigenschaft zuzuweisen:
> var obj = {}; // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123Und Sie können ihn verwenden, um Methoden aufzurufen:
> 'hello'.toUpperCase() 'HELLO'
Im obigen Beispiel haben wir die Methode toUpperCase() für den Wert 'hello' aufgerufen.
JavaScript trifft eine etwas willkürliche Unterscheidung zwischen Werten:
null und undefined.Ein Hauptunterschied zwischen den beiden besteht darin, wie sie verglichen werden; jedes Objekt hat eine eindeutige Identität und ist nur (strikt) gleich sich selbst:
> var obj1 = {}; // an empty object
> var obj2 = {}; // another empty object
> obj1 === obj2
false
> obj1 === obj1
trueIm Gegensatz dazu gelten alle primitiven Werte, die denselben Wert kodieren, als gleich
> var prim1 = 123; > var prim2 = 123; > prim1 === prim2 true
Die nächsten beiden Abschnitte erklären primitive Werte und Objekte im Detail.
Die folgenden sind alle primitiven Werte (oder kurz Primitives):
true, false (siehe Booleans)1736, 1.351 (siehe Zahlen)'abc', "abc" (siehe Strings)undefined, null (siehe undefined und null)Primitives haben die folgenden Merkmale:
Der „Inhalt“ wird verglichen
> 3 === 3 true > 'abc' === 'abc' true
Eigenschaften können nicht geändert, hinzugefügt oder entfernt werden:
> var str = 'abc'; > str.length = 1; // try to change property `length` > str.length // ⇒ no effect 3 > str.foo = 3; // try to create property `foo` > str.foo // ⇒ no effect, unknown property undefined
(Das Lesen einer unbekannten Eigenschaft gibt immer undefined zurück.)
Alle nicht-primitiven Werte sind Objekte. Die gängigsten Arten von Objekten sind:
Einfache Objekte, die durch Objektliterale erstellt werden können (siehe Einzelne Objekte)
{firstName:'Jane',lastName:'Doe'}
Das vorherige Objekt hat zwei Eigenschaften: Der Wert der Eigenschaft firstName ist 'Jane' und der Wert der Eigenschaft lastName ist 'Doe'.
Arrays, die durch Array-Literale erstellt werden können (siehe Arrays)
['apple','banana','cherry']
Das vorherige Array hat drei Elemente, auf die über numerische Indizes zugegriffen werden kann. Zum Beispiel ist der Index von 'apple' 0.
Reguläre Ausdrücke, die durch reguläre Ausdrucks-Literale erstellt werden können (siehe Reguläre Ausdrücke)
/^a+b+$/Objekte haben die folgenden Merkmale:
Identitäten werden verglichen; jeder Wert hat seine eigene Identität
> ({} === {}) // two different empty objects
false
> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
trueSie können Eigenschaften normalerweise frei ändern, hinzufügen und entfernen (siehe Einzelne Objekte)
> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123Die meisten Programmiersprachen haben Werte, die fehlende Informationen bezeichnen. JavaScript hat zwei solche „Nicht-Werte“, undefined und null:
undefined bedeutet „kein Wert“. Uninitialisierte Variablen sind undefined
> var foo; > foo undefined
Fehlende Parameter sind undefined
> function f(x) { return x }
> f()
undefinedWenn Sie eine nicht existierende Eigenschaft lesen, erhalten Sie undefined
> var obj = {}; // empty object
> obj.foo
undefinednull bedeutet „kein Objekt“. Es wird als Nicht-Wert verwendet, wenn ein Objekt erwartet wird (Parameter, das letzte in einer Kette von Objekten usw.).undefined und null haben keine Eigenschaften, nicht einmal Standardmethoden wie toString().
Funktionen erlauben es Ihnen normalerweise, einen fehlenden Wert entweder über undefined oder null anzugeben. Sie können dasselbe über eine explizite Prüfung tun:
if(x===undefined||x===null){...}
Sie können auch die Tatsache ausnutzen, dass sowohl undefined als auch null als false gelten
if(!x){...}
false, 0, NaN und '' gelten ebenfalls als false (siehe Truthy und Falsy).
Es gibt zwei Operatoren zur Kategorisierung von Werten: typeof wird hauptsächlich für primitive Werte verwendet, während instanceof für Objekte verwendet wird.
typeof sieht so aus
typeofvalue
Es gibt einen String zurück, der den „Typ“ von value beschreibt. Hier sind einige Beispiele
> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'Die folgende Tabelle listet alle Ergebnisse von typeof auf
| Operand | Ergebnis |
|
|
|
|
Boolean-Wert |
|
Zahlenwert |
|
String-Wert |
|
Funktion |
|
Alle anderen normalen Werte |
|
(Vom Engine erzeugter Wert) | JavaScript-Engines dürfen Werte erzeugen, für die |
typeof null gibt 'object' zurück, was ein Fehler ist, der nicht behoben werden kann, da er bestehenden Code brechen würde. Es bedeutet nicht, dass null ein Objekt ist.
instanceof sieht so aus
valueinstanceofConstr
Es gibt true zurück, wenn value ein Objekt ist, das vom Konstruktor Constr erstellt wurde (siehe Konstruktoren: Fabriken für Objekte). Hier sind einige Beispiele
> var b = new Bar(); // object created by constructor Bar
> b instanceof Bar
true
> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object // Array is a subconstructor of Object
true
> undefined instanceof Object
false
> null instanceof Object
falseDer primitive boolesche Typ umfasst die Werte true und false. Die folgenden Operatoren erzeugen Booleans:
&& (Und), || (Oder)! (Nicht)Vergleichsoperatoren
===, !==, ==, !=>, >=, <, <=Immer wenn JavaScript einen booleschen Wert erwartet (z. B. für die Bedingung einer if-Anweisung), kann jeder Wert verwendet werden. Er wird als true oder false interpretiert. Die folgenden Werte werden als false interpretiert
undefined, nullfalse0, NaN''Alle anderen Werte (einschließlich aller Objekte!) werden als true betrachtet. Werte, die als false interpretiert werden, werden falsy genannt, und Werte, die als true interpretiert werden, werden truthy genannt. Boolean(), als Funktion aufgerufen, konvertiert seinen Parameter in einen Boolean. Sie können es verwenden, um zu testen, wie ein Wert interpretiert wird
> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
trueBinäre logische Operatoren in JavaScript sind short-circuiting. Das heißt, wenn der erste Operand ausreicht, um das Ergebnis zu bestimmen, wird der zweite Operand nicht ausgewertet. Zum Beispiel werden in den folgenden Ausdrücken die Funktion foo() niemals aufgerufen:
false&&foo()true||foo()
Darüber hinaus geben binäre logische Operatoren entweder einen ihrer Operanden zurück – der ein Boolean sein kann oder nicht. Eine Prüfung auf Wahrheit wird verwendet, um zu bestimmen, welcher
&&)Wenn der erste Operand falsy ist, gib ihn zurück. Andernfalls gib den zweiten Operanden zurück:
> NaN && 'abc' NaN > 123 && 'abc' 'abc'
||)Wenn der erste Operand truthy ist, gib ihn zurück. Andernfalls gib den zweiten Operanden zurück:
> 'abc' || 123 'abc' > '' || 123 123
JavaScript hat zwei Arten von Gleichheit:
== und !==== und !==Normale Gleichheit betrachtet (zu) viele Werte als gleich (die Details werden in Normale (nachgiebige) Gleichheit (==, !=) erklärt), was Fehler verbergen kann. Daher wird immer die Verwendung von strikter Gleichheit empfohlen.
Alle Zahlen in JavaScript sind Gleitkommazahlen:
> 1 === 1.0 true
Spezielle Zahlen umfassen die folgenden:
NaN („not a number“)Ein Fehlerwert
> Number('xyz') // 'xyz' can’t be converted to a number
NaNInfinity
Auch meist ein Fehlerwert:
> 3 / 0 Infinity > Math.pow(2, 1024) // number too large Infinity
Infinity ist größer als jede andere Zahl (außer NaN). Ebenso ist -Infinity kleiner als jede andere Zahl (außer NaN). Das macht diese Zahlen nützlich als Standardwerte (z. B. wenn Sie nach einem Minimum oder Maximum suchen).
JavaScript hat die folgenden arithmetischen Operatoren (siehe Arithmetische Operatoren)
number1 + number2number1 - number2number1 * number2number1 / number2number1 % number2++variable, variable++--variable, variable---value+valueDas globale Objekt Math (siehe Math) bietet weitere arithmetische Operationen über Funktionen.
JavaScript hat auch Operatoren für Bit-Operationen (z. B. Bitweises Und; siehe Bitweise Operatoren).
Zeichenketten können direkt über Zeichenkettenliterale erstellt werden. Diese Literale werden durch einfache oder doppelte Anführungszeichen begrenzt. Der Backslash (\) maskiert Zeichen und erzeugt einige Steuerzeichen. Hier sind einige Beispiele:
'abc'"abc"'Did she say "Hello"?'"Did she say \"Hello\"?"'That\'s nice!'"That's nice!"'Line 1\nLine 2'// newline'Backlash: \\'
Einzelne Zeichen werden über eckige Klammern abgerufen:
> var str = 'abc'; > str[1] 'b'
Die Eigenschaft length zählt die Anzahl der Zeichen in der Zeichenkette:
> 'abc'.length 3
Wie alle Primitives sind Zeichenketten unveränderlich; Sie müssen eine neue Zeichenkette erstellen, wenn Sie eine vorhandene ändern möchten.
Zeichenketten werden über den Plusoperator (+) verkettet, der den anderen Operanden in eine Zeichenkette umwandelt, wenn einer der Operanden eine Zeichenkette ist:
> var messageCount = 3; > 'You have ' + messageCount + ' messages' 'You have 3 messages'
Um Zeichenketten in mehreren Schritten zu verketten, verwenden Sie den Operator +=:
> var str = ''; > str += 'Multiple '; > str += 'pieces '; > str += 'are concatenated.'; > str 'Multiple pieces are concatenated.'
Zeichenketten haben viele nützliche Methoden (siehe String-Prototyp-Methoden). Hier sind einige Beispiele
> 'abc'.slice(1) // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'
> '\t xyz '.trim() // trim whitespace
'xyz'
> 'mjölnir'.toUpperCase()
'MJÖLNIR'
> 'abc'.indexOf('b') // find a string
1
> 'abc'.indexOf('x')
-1Bedingungen und Schleifen in JavaScript werden in den folgenden Abschnitten vorgestellt.
Die Anweisung if hat eine then-Klausel und eine optionale else-Klausel, die je nach boolescher Bedingung ausgeführt werden:
if(myvar===0){// then}if(myvar===0){// then}else{// else}if(myvar===0){// then}elseif(myvar===1){// else-if}elseif(myvar===2){// else-if}else{// else}
Ich empfehle, immer geschweifte Klammern zu verwenden (sie bezeichnen Blöcke von null oder mehr Anweisungen). Sie müssen dies jedoch nicht tun, wenn eine Klausel nur eine einzige Anweisung ist (dasselbe gilt für die Kontrollflussanweisungen for und while):
if(x<0)return-x;
Das Folgende ist eine switch-Anweisung. Der Wert von fruit bestimmt, welcher case ausgeführt wird:
switch(fruit){case'banana':// ...break;case'apple':// ...break;default:// all other cases// ...}
Der „Operand“ nach case kann jeder Ausdruck sein; er wird über === mit dem Parameter von switch verglichen.
Die for-Schleife hat das folgende Format:
for(⟦«init»⟧;⟦«condition»⟧;⟦«post_iteration»⟧)«statement»
init wird am Anfang der Schleife ausgeführt. condition wird vor jeder Schleifeniteration überprüft; wenn sie false wird, wird die Schleife beendet. post_iteration wird nach jeder Schleifeniteration ausgeführt.
Dieses Beispiel gibt alle Elemente des Arrays arr auf der Konsole aus
for(vari=0;i<arr.length;i++){console.log(arr[i]);}
Die while-Schleife führt ihren Körper weiter aus, solange ihre Bedingung gilt:
// Same as for loop above:vari=0;while(i<arr.length){console.log(arr[i]);i++;}
do{// ...}while(condition);
In allen Schleifen:
break verlässt die Schleife.continue startet eine neue Schleifeniteration.Eine Möglichkeit, eine Funktion zu definieren, ist über eine Funktionsdeklaration
functionadd(param1,param2){returnparam1+param2;}
Der vorherige Code definiert eine Funktion, add, die zwei Parameter hat, param1 und param2, und die Summe beider Parameter zurückgibt. So ruft man diese Funktion auf
> add(6, 1)
7
> add('a', 'b')
'ab'Eine andere Möglichkeit, add() zu definieren, ist die Zuweisung eines Funktionsausdrucks zu einer Variablen add:
varadd=function(param1,param2){returnparam1+param2;};
Ein Funktionsausdruck erzeugt einen Wert und kann daher verwendet werden, um Funktionen direkt als Argumente an andere Funktionen zu übergeben
someOtherFunction(function(p1,p2){...});
Funktionsdeklarationen werden gehoben – d. h. ihre Gesamtheit wird an den Anfang des aktuellen Geltungsbereichs verschoben. Das erlaubt Ihnen, auf später deklarierte Funktionen zu verweisen:
functionfoo(){bar();// OK, bar is hoistedfunctionbar(){...}}
Beachten Sie, dass var-Deklarationen zwar ebenfalls gehoben werden (siehe Variablen werden gehoben), aber die Zuweisungen, die sie vornehmen, nicht
functionfoo(){bar();// Not OK, bar is still undefinedvarbar=function(){// ...};}
Sie können jede Funktion in JavaScript mit einer beliebigen Anzahl von Argumenten aufrufen; die Sprache wird nie klagen. Sie wird jedoch alle Parameter über die spezielle Variable arguments verfügbar machen. arguments sieht aus wie ein Array, hat aber keine der Array-Methoden:
> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0] // read element at index 0
'a'Verwenden wir die folgende Funktion, um zu untersuchen, wie zu viele oder zu wenige Parameter in JavaScript behandelt werden (die Funktion toArray() wird in Argumente in ein Array konvertieren gezeigt)
functionf(x,y){console.log(x,y);returntoArray(arguments);}
Zusätzliche Parameter werden ignoriert (außer von arguments)
> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]Fehlende Parameter erhalten den Wert undefined:
> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]Das Folgende ist ein gängiges Muster zum Zuweisen von Standardwerten zu Parametern:
functionpair(x,y){x=x||0;// (1)y=y||0;return[x,y];}
In Zeile (1) gibt der Operator || x zurück, wenn er truthy ist (nicht null, undefined usw.). Andernfalls gibt er den zweiten Operanden zurück
> pair() [ 0, 0 ] > pair(3) [ 3, 0 ] > pair(3, 5) [ 3, 5 ]
Wenn Sie eine Arity (eine bestimmte Anzahl von Parametern) erzwingen möchten, können Sie arguments.length überprüfen:
functionpair(x,y){if(arguments.length!==2){thrownewError('Need exactly 2 arguments');}...}
arguments ist kein Array, es ist nur array-ähnlich (siehe Array-ähnliche Objekte und generische Methoden). Es hat eine Eigenschaft length, und Sie können über Indizes in eckigen Klammern auf seine Elemente zugreifen. Sie können jedoch keine Elemente entfernen oder eine der Array-Methoden darauf aufrufen. Daher müssen Sie manchmal arguments in ein Array konvertieren, was die folgende Funktion tut (sie wird in Array-ähnliche Objekte und generische Methoden erklärt)
functiontoArray(arrayLikeObject){returnArray.prototype.slice.call(arrayLikeObject);}
Der häufigste Weg zur Behandlung von Ausnahmen (siehe Kapitel 14) ist wie folgt
functiongetPerson(id){if(id<0){thrownewError('ID must not be negative: '+id);}return{id:id};// normally: retrieved from database}functiongetPersons(ids){varresult=[];ids.forEach(function(id){try{varperson=getPerson(id);result.push(person);}catch(exception){console.log(exception);}});returnresult;}
Die try-Klausel umschließt kritischen Code, und die catch-Klausel wird ausgeführt, wenn innerhalb der try-Klausel eine Ausnahme ausgelöst wird. Bei Verwendung des vorherigen Codes:
> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]Der strikte Modus (siehe Strikter Modus) ermöglicht mehr Warnungen und macht JavaScript zu einer saubereren Sprache (der Nicht-Strikte Modus wird manchmal als „sloppy mode“ bezeichnet). Um ihn zu aktivieren, tippen Sie die folgende Zeile zuerst in eine JavaScript-Datei oder ein <script>-Tag:
'use strict';
Sie können den strikten Modus auch pro Funktion aktivieren:
functionfunctionInStrictMode(){'use strict';}
In JavaScript deklarieren Sie Variablen mit var, bevor Sie sie verwenden:
> var x; > x undefined > y ReferenceError: y is not defined
Sie können mehrere Variablen mit einer einzigen var-Anweisung deklarieren und initialisieren
varx=1,y=2,z=3;
Aber ich empfehle, eine Anweisung pro Variable zu verwenden (der Grund wird in Syntax erklärt). Daher würde ich die vorherige Anweisung wie folgt umschreiben
varx=1;vary=2;varz=3;
Aufgrund des Hoisting (siehe Variablen werden gehoben) ist es normalerweise am besten, Variablen am Anfang einer Funktion zu deklarieren.
Der Geltungsbereich einer Variablen ist immer die vollständige Funktion (im Gegensatz zum aktuellen Block). Zum Beispiel:
functionfoo(){varx=-512;if(x<0){// (1)vartmp=-x;...}console.log(tmp);// 512}
Wir sehen, dass die Variable tmp nicht auf den Block beschränkt ist, der in Zeile (1) beginnt; sie existiert bis zum Ende der Funktion.
Jede Variablendeklaration wird gehoben: Die Deklaration wird an den Anfang der Funktion verschoben, aber Zuweisungen, die sie vornimmt, bleiben an ihrem Platz. Als Beispiel betrachten wir die Variablendeklaration in Zeile (1) in der folgenden Funktion:
functionfoo(){console.log(tmp);// undefinedif(false){vartmp=3;// (1)}}
Intern wird die vorherige Funktion wie folgt ausgeführt
functionfoo(){vartmp;// hoisted declarationconsole.log(tmp);if(false){tmp=3;// assignment stays put}}
Jede Funktion bleibt mit den Variablen der sie umgebenden Funktionen verbunden, auch nachdem sie den Geltungsbereich verlassen hat, in dem sie erstellt wurde. Zum Beispiel:
functioncreateIncrementor(start){returnfunction(){// (1)start++;returnstart;}}
Die Funktion, die in Zeile (1) beginnt, verlässt den Kontext, in dem sie erstellt wurde, aber sie bleibt mit einer Live-Version von start verbunden
> var inc = createIncrementor(5); > inc() 6 > inc() 7 > inc() 8
Ein Closure ist eine Funktion plus die Verbindung zu den Variablen ihrer umgebenden Geltungsbereiche. Daher ist das, was createIncrementor() zurückgibt, ein Closure.
Manchmal möchten Sie einen neuen Variablen-Geltungsbereich einführen – zum Beispiel, um zu verhindern, dass eine Variable global wird. In JavaScript können Sie keinen Block dafür verwenden; Sie müssen eine Funktion verwenden. Aber es gibt ein Muster, um eine Funktion blockähnlich zu verwenden. Es heißt IIFE (immediately invoked function expression, ausgesprochen „iffy“)
(function(){// open IIFEvartmp=...;// not a global variable}());// close IIFE
Stellen Sie sicher, dass Sie das vorherige Beispiel genau so eingeben, wie es gezeigt wird (abgesehen von den Kommentaren). Ein IIFE ist ein Funktionsausdruck, der sofort nach seiner Definition aufgerufen wird. Innerhalb der Funktion existiert ein neuer Geltungsbereich, der verhindert, dass tmp global wird. Konsultieren Sie Einführung eines neuen Geltungsbereichs über ein IIFE für Details zu IIFEs.
Closures behalten ihre Verbindungen zu äußeren Variablen, was manchmal nicht erwünscht ist:
varresult=[];for(vari=0;i<5;i++){result.push(function(){returni});// (1)}console.log(result[1]());// 5 (not 1)console.log(result[3]());// 5 (not 3)
Der in Zeile (1) zurückgegebene Wert ist immer der aktuelle Wert von i, nicht der Wert, den es hatte, als die Funktion erstellt wurde. Nachdem die Schleife beendet ist, hat i den Wert 5, weshalb alle Funktionen im Array diesen Wert zurückgeben. Wenn Sie möchten, dass die Funktion in Zeile (1) einen Schnappschuss des aktuellen Wertes von i erhält, können Sie ein IIFE verwenden
for(vari=0;i<5;i++){(function(){vari2=i;// copy current iresult.push(function(){returni2});}());}
Dieser Abschnitt behandelt zwei grundlegende objektorientierte Mechanismen von JavaScript: einzelne Objekte und Konstruktoren (die Fabriken für Objekte sind, ähnlich wie Klassen in anderen Sprachen).
Wie alle Werte haben Objekte Eigenschaften. Tatsächlich kann man ein Objekt als eine Menge von Eigenschaften betrachten, wobei jede Eigenschaft ein (Schlüssel, Wert)-Paar ist. Der Schlüssel ist eine Zeichenkette, und der Wert ist ein beliebiger JavaScript-Wert.
In JavaScript können Sie einfache Objekte direkt über Objekt-Literale erstellen.
'use strict';varjane={name:'Jane',describe:function(){return'Person named '+this.name;}};
Das vorhergehende Objekt hat die Eigenschaften name und describe. Sie können Eigenschaften lesen („holen“) und schreiben („setzen“).
> jane.name // get 'Jane' > jane.name = 'John'; // set > jane.newProperty = 'abc'; // property created automatically
Funktionswertige Eigenschaften wie describe werden als Methoden bezeichnet. Sie verwenden this, um auf das Objekt zu verweisen, das zur Aufrufung verwendet wurde:
> jane.describe() // call method 'Person named John' > jane.name = 'Jane'; > jane.describe() 'Person named Jane'
Der Operator in prüft, ob eine Eigenschaft existiert:
> 'newProperty' in jane true > 'foo' in jane false
Wenn Sie eine nicht existierende Eigenschaft lesen, erhalten Sie den Wert undefined. Daher könnten die beiden vorherigen Prüfungen auch so durchgeführt werden:[2]
> jane.newProperty !== undefined true > jane.foo !== undefined false
Der Operator delete entfernt eine Eigenschaft:
> delete jane.newProperty true > 'newProperty' in jane false
Ein Eigenschaftsschlüssel kann eine beliebige Zeichenkette sein. Bisher haben wir Eigenschaftsschlüssel in Objekt-Literalen und nach dem Punkt-Operator gesehen. Sie können sie jedoch nur auf diese Weise verwenden, wenn sie Bezeichner sind (siehe Bezeichner und Variablennamen). Wenn Sie andere Zeichenketten als Schlüssel verwenden möchten, müssen Sie sie in einem Objekt-Literal in Anführungszeichen setzen und eckige Klammern verwenden, um die Eigenschaft zu holen und zu setzen:
> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;Eckige Klammern ermöglichen es Ihnen auch, den Schlüssel einer Eigenschaft zu berechnen:
> var obj = { hello: 'world' };
> var x = 'hello';
> obj[x]
'world'
> obj['hel'+'lo']
'world'Wenn Sie eine Methode extrahieren, verliert sie ihre Verbindung zum Objekt. Alleinstehend ist die Funktion keine Methode mehr, und this hat den Wert undefined (im Strict Mode).
Als Beispiel kehren wir zum früheren Objekt jane zurück.
'use strict';varjane={name:'Jane',describe:function(){return'Person named '+this.name;}};
Wir möchten die Methode describe von jane extrahieren, sie in eine Variable func legen und aufrufen. Das funktioniert jedoch nicht.
> var func = jane.describe; > func() TypeError: Cannot read property 'name' of undefined
Die Lösung besteht darin, die Methode bind() zu verwenden, die alle Funktionen haben. Sie erstellt eine neue Funktion, deren this immer den angegebenen Wert hat:
> var func2 = jane.describe.bind(jane); > func2() 'Person named Jane'
Jede Funktion hatihre eigene spezielle Variable this. Dies ist unbequem, wenn Sie eine Funktion innerhalb einer Methode verschachteln, da Sie von der Funktion aus nicht auf this der Methode zugreifen können. Das Folgende ist ein Beispiel, bei dem wir forEach mit einer Funktion aufrufen, um ein Array zu durchlaufen:
varjane={name:'Jane',friends:['Tarzan','Cheeta'],logHiToFriends:function(){'use strict';this.friends.forEach(function(friend){// `this` is undefined hereconsole.log(this.name+' says hi to '+friend);});}}
Der Aufruf von logHiToFriends führt zu einem Fehler.
> jane.logHiToFriends() TypeError: Cannot read property 'name' of undefined
Schauen wir uns zwei Möglichkeiten an, dies zu beheben. Erstens könnten wir this in einer anderen Variablen speichern.
logHiToFriends:function(){'use strict';varthat=this;this.friends.forEach(function(friend){console.log(that.name+' says hi to '+friend);});}
Oder forEach hat einen zweiten Parameter, mit dem Sie einen Wert für this angeben können.
logHiToFriends:function(){'use strict';this.friends.forEach(function(friend){console.log(this.name+' says hi to '+friend);},this);}
Funktionsausdrücke werden häufig als Argumente in Funktionsaufrufen in JavaScript verwendet. Seien Sie immer vorsichtig, wenn Sie von einem dieser Funktionsausdrücke auf this verweisen.
Bis jetzt denken Sie vielleicht, dass JavaScript-Objekte nur Abbildungen von Zeichenketten zu Werten sind, eine Vorstellung, die durch die Objekt-Literale von JavaScript nahegelegt wird, die wie die Map/Dictionary-Literale anderer Sprachen aussehen. JavaScript-Objekte unterstützen jedoch auch ein Merkmal, das wirklich objektorientiert ist: Vererbung. Dieser Abschnitt erklärt nicht vollständig, wie die Vererbung in JavaScript funktioniert, aber er zeigt ein einfaches Muster, um Ihnen den Einstieg zu erleichtern. Konsultieren SieKapitel 17, wenn Sie mehr erfahren möchten.
Neben der Tatsache, dass sie „echte“ Funktionen und Methoden sind, spielen Funktionen eine weitere Rolle in JavaScript: Sie werden zu Konstruktoren — Fabriken für Objekte —, wenn sie über den Operator new aufgerufen werden. Konstruktoren sind somit ein grobes Analogon zu Klassen in anderen Sprachen. Gemäß Konvention beginnen die Namen von Konstruktoren mit Großbuchstaben. Zum Beispiel:
// Set up instance datafunctionPoint(x,y){this.x=x;this.y=y;}// MethodsPoint.prototype.dist=function(){returnMath.sqrt(this.x*this.x+this.y*this.y);};
Wir können sehen, dass ein Konstruktor zwei Teile hat. Erstens richtet die Funktion Point die Instanzdaten ein. Zweitens enthält die Eigenschaft Point.prototype ein Objekt mit den Methoden. Die erstgenannten Daten sind spezifisch für jede Instanz, während die letztgenannten Daten von allen Instanzen gemeinsam genutzt werden.
Um Point zu verwenden, rufen wir es über den Operator new auf:
> var p = new Point(3, 5); > p.x 3 > p.dist() 5.830951894845301
p ist eine Instanz von Point.
> p instanceof Point true
Arrays sind Sequenzen von Elementen, auf die über ganzzahlige Indizes ab Null zugegriffen werden kann.
Array-Literale sind praktisch für das Erstellen von Arrays:
> var arr = [ 'a', 'b', 'c' ];
Das vorhergehende Array hat drei Elemente: die Zeichenketten 'a', 'b' und 'c'. Sie können über ganzzahlige Indizes darauf zugreifen.
> arr[0] 'a' > arr[0] = 'x'; > arr [ 'x', 'b', 'c' ]
Die Eigenschaft length gibt an, wie viele Elemente ein Array hat. Sie können sie zum Anhängen von Elementen und zum Entfernen von Elementen verwenden:
> var arr = ['a', 'b']; > arr.length 2 > arr[arr.length] = 'c'; > arr [ 'a', 'b', 'c' ] > arr.length 3 > arr.length = 1; > arr [ 'a' ]
Der Operator in funktioniert auch für Arrays:
> var arr = [ 'a', 'b', 'c' ]; > 1 in arr // is there an element at index 1? true > 5 in arr // is there an element at index 5? false
Beachten Sie, dass Arrays Objekte sind und somit Objekteigenschaften haben können:
> var arr = []; > arr.foo = 123; > arr.foo 123
Arrays haben viele Methoden (siehe Array-Prototypmethoden). Hier sind einige Beispiele.
> var arr = [ 'a', 'b', 'c' ];
> arr.slice(1, 2) // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]
> arr.push('x') // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]
> arr.pop() // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]
> arr.shift() // remove first element
'a'
> arr
[ 'b', 'c' ]
> arr.unshift('x') // prepend an element
3
> arr
[ 'x', 'b', 'c' ]
> arr.indexOf('b') // find the index of an element
1
> arr.indexOf('y')
-1
> arr.join('-') // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'Es gibt mehrere Array-Methoden zum Iterieren über Elemente (siehe Array-Iteration). Die beiden wichtigsten sind forEach und map.
forEach iteriert über ein Array und übergibt das aktuelle Element und seinen Index an eine Funktion.
['a','b','c'].forEach(function(elem,index){// (1)console.log(index+'. '+elem);});
Der vorhergehende Code erzeugt die folgende Ausgabe.
0. a 1. b 2. c
Beachten Sie, dass die Funktion in Zeile (1) Argumente frei ignorieren kann. Sie könnte zum Beispiel nur den Parameter elem haben.
map erstellt ein neues Array, indem es eine Funktion auf jedes Element eines vorhandenen Arrays anwendet:
> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]JavaScript hat integrierte Unterstützung für reguläre Ausdrücke (Kapitel 19 verweist auf Tutorials und erklärt detaillierter, wie sie funktionieren). Sie werden durch Schrägstriche begrenzt:
/^abc$//[A-Za-z0-9]+/
> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]Das zurückgegebene Array enthält die vollständige Übereinstimmung an Index 0, die Erfassung der ersten Gruppe an Index 1 und so weiter. Es gibt eine Möglichkeit (diskutiert in RegExp.prototype.exec: Erfassungsgruppen), diese Methode wiederholt aufzurufen, um alle Übereinstimmungen zu erhalten.
> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]') '[a] [bbb]'
Der erste Parameter von replace muss ein regulärer Ausdruck mit einem /g-Flag sein; andernfalls wird nur das erste Vorkommen ersetzt. Es gibt auch eine Möglichkeit (wie in String.prototype.replace: Suchen und Ersetzen) eine Funktion zu verwenden, um den Ersatz zu berechnen.
Math (siehe Kapitel 21) ist ein Objekt mit arithmetischen Funktionen. Hier sind einige Beispiele:
> Math.abs(-2) 2 > Math.pow(3, 2) // 3 to the power of 2 9 > Math.max(2, -1, 5) 5 > Math.round(1.9) 2 > Math.PI // pre-defined constant for π 3.141592653589793 > Math.cos(Math.PI) // compute the cosine for 180° -1
Die Standardbibliothek von JavaScript ist relativ spartanisch, aber es gibt mehr Dinge, die Sie verwenden können:
Date (siehe Kapitel 20)JSON (siehe Kapitel 22)console.* Methoden (siehe Die Konsolen-API)