Dieser Abschnitt gibt einen kurzen Überblick über JavaScript, um Ihnen zu helfen zu verstehen, warum es so ist.
ECMAScript ist der offizielle Name für JavaScript. Ein neuer Name wurde notwendig, weil es eine Marke für JavaScript gibt (ursprünglich von Sun, jetzt von Oracle gehalten). Momentan ist Mozilla eines der wenigen Unternehmen, die den Namen JavaScript offiziell verwenden dürfen, weil sie vor langer Zeit eine Lizenz erhalten hat. Für die allgemeine Verwendung gelten folgende Regeln:
Der Schöpfer von JavaScript, Brendan Eich, hatte keine andere Wahl, als die Sprache sehr schnell zu erstellen (oder andere, schlechtere Technologien wären von Netscape übernommen worden). Er lieh sich mehrere Programmiersprachen: Java (Syntax, primitive Werte gegen Objekte), Scheme und AWK (Erstklasse-Funktionen), Self (prototypische Vererbung) und Perl und Python (Strings, Arrays und reguläre Ausdrücke).
JavaScript hatte bis ECMAScript 3 keine Ausnahmebehandlung, was erklärt, warum die Sprache so oft automatisch Werte konvertiert und so oft stillschweigend versagt: Sie konnte zunächst keine Ausnahmen werfen.
Auf der einen Seite hat JavaScript Eigenschaften und fehlt einiges an Funktionalität (Block-Scope-Variablen, Module, Unterstützung für Unterklassen usw.). Auf der anderen Seite hat es mehrere leistungsstarke Funktionen, mit denen Sie diese Probleme umgehen können. In anderen Sprachen lernen Sie Sprachfunktionen. In JavaScript lernen Sie stattdessen oft Muster.
Angesichts seiner Einflüsse ist es nicht verwunderlich, dass JavaScript einen Programmierstil ermöglicht, der eine Mischung aus funktionaler Programmierung (Funktionen höherer Ordnung; integrierte Karte, reduzieren usw.) und objektorientierter Programmierung (Objekte, Vererbung) ist.
Dieser Abschnitt erklärt grundlegende syntaktische Prinzipien von JavaScript.
Ein paar Beispiele für die Syntax:
// Two slashes start single-line comments
var x; // declaring a variable
x = 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 statement
if (x === 0) { // Is `x` equal to zero?
x = 123;
}
// Defining function `baz` with parameters `a` and `b`
function baz(a, b) {
return a + b;
}
Beachten Sie die zwei verschiedenen Verwendungen des Gleichheitszeichens:
Um die Syntax von JavaScript zu verstehen, sollten Sie wissen, dass es zwei Hauptsyntaxkategorien hat: Statements und Expressions:
var foo;
3 * 7
Die Unterscheidung zwischen Statements und Expressions wird am besten durch die Tatsache veranschaulicht, dass JavaScript zwei verschiedene Möglichkeiten hat, if-then-else
var x;
if (y >= 0) {
x = y;
} else {
x = -y;
}
oder als Ausdruck:
var x = y >= 0 ? y : -y;
Sie können letzteres als Funktionsargument verwenden (aber nicht das erstere):
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 Funktionsruf foo(7, 1) ist ein Ausdruck.
Semikolon ist in JavaScript optional. Allerdings empfehle ich, sie immer einzubeziehen, da JavaScript sonst das Ende einer Anweisung falsch erraten kann. Die Details werden in Automatic Semicolon Insertion erklärt.
Semikolon beendet Aussagen, aber nicht Blöcke. Es gibt einen Fall, in dem Sie ein Semikolon nach einem Block sehen: ein Funktionsausdruck ist ein Ausdruck, der mit einem Block endet. Wenn ein solcher Ausdruck zuletzt in einer Aussage erscheint, wird er von einem Semikolon gefolgt:
// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { }; // function expr. inside var decl.
JavaScript hat zwei Arten von Kommentaren: Einzeilenkommentare und mehrzeilige Kommentare. Einzeilenkommentare beginnen mit // und werden am Ende der Zeile beendet:
x++; // single-line comment
Kommentare mit mehreren Zeilen werden durch /* und */ begrenzt:
/* This is
a multiline
comment.
*/
Variablen in JavaScript werden erklärt, bevor sie verwendet werden:
var foo; // declare variable `foo`
Sie können eine Variable deklarieren und gleichzeitig einen Wert zuweisen:
var foo = 6;
Sie können auch einen Wert einer vorhandenen Variablen zuweisen:
foo = 4; // change variable `foo`
Es gibt zusammengesetzte Zuweisungsoperatoren wie +=. Die folgenden beiden Zuweisungen sind gleichwertig:
x += 1;
x = x + 1;
Identifikatoren sind Namen, die in JavaScript verschiedene syntaktische Rollen spielen.
Grob gesagt kann das erste Zeichen eines Identifikators jeder beliebige Unicode-Buchstabe, ein Dollarzeichen ($) oder ein Unterzeichen (_) sein.
arg0
_tmp
$elem
π
Die folgenden Identifikatoren sind reservierte Wörter, sie sind Teil der Syntax und können nicht als Variablennamen (einschließlich Funktionsnamen und Parameternamen) verwendet werden:
Die folgenden drei Kennungen sind keine reservierten Wörter, aber Sie sollten sie so behandeln, als wären sie:
Schließlich sollten Sie sich auch von den Namen von Standard-globalen Variablen fernhalten. Sie können sie für lokale Variablen verwenden, ohne etwas zu brechen, aber Ihr Code wird immer noch verwirrend.
JavaScript hat viele Werte, die wir von Programmiersprachen erwarten: Booleaner, Zahlen, Zeichenfolgen, Arrays und so weiter. Alle Werte in JavaScript haben Eigenschaften. Jede Eigenschaft hat einen Schlüssel (oder einen Namen) und einen Wert. Sie können sich Eigenschaften wie Felder eines Datensatzes vorstellen.
value.propKey
Zum Beispiel hat die Zeichenfolge
> var str = 'abc';
> str.length
3
Das Vorabwort kann auch so geschrieben werden:
> 'abc'.length
3
The dot operator is also used to assign a value to a property:
> var obj = {}; // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123
Und man kann damit Methoden aufrufen:
> 'hello'.toUpperCase()
'HELLO'
In dem vorherigen Beispiel haben wir die Methode toUpperCase() auf den Wert
JavaScript macht eine etwas willkürliche Unterscheidung zwischen den Werten:
> var obj1 = {}; // an empty object
> var obj2 = {}; // another empty object
> obj1 === obj2
false
> obj1 === obj1
true
Im Gegensatz dazu gelten alle primitiven Werte, die denselben Wert codieren, als gleich:
> var prim1 = 123;
> var prim2 = 123;
> prim1 === prim2
true
In den nächsten beiden Abschnitten werden primitive Werte und Objekte detaillierter erläutert.
Die folgenden sind alle primitiven Werte (oder kurz primitives):
Primitive haben folgende Eigenschaften:
Der
> 3 === 3
true
> 'abc' === 'abc'
true
Immer unveränderlich 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 undefiniert zurück.)
Alle nicht-primitiven Werte sind Objekte. Die häufigsten Objekttypen sind:
{
firstName: 'Jane',
lastName: 'Doe'
}
Das vorhergehende Objekt hat zwei Eigenschaften: der Wert der Eigenschaft firstName ist
[ 'apple', 'banana', 'cherry' ]
Das vorhergehende Array hat drei Elemente, die über numerische Indizes abgerufen werden können.
/^a+b+$/
Die Objekte weisen folgende Merkmale auf:
Identitäten werden verglichen; jeder Wert hat seine eigene Identität:
> ({} === {}) // two different empty objects
false
> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true
Normalerweise können Sie Eigenschaften frei ändern, hinzufügen und entfernen (siehe Einzelobjekte):
> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123
Die meisten Programmiersprachen haben Werte, die fehlende Informationen kennzeichnen.
> var foo;
> foo
undefined
Die fehlenden Parameter sind nicht definiert:
> function f(x) { return x }
> f()
undefined
Wenn man eine nicht existierende Eigenschaft liest, wird man undefiniert:
> var obj = {}; // empty object
> obj.foo
undefined
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.
if (x === undefined || x === null) {
...
}
Sie können auch die Tatsache ausnutzen, dass sowohl undefiniert als auch null als falsch betrachtet werden:
if (!x) {
...
}
false, 0, NaN und
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. Der Typ sieht so aus:
typeof value
Er gibt eine Zeichenfolge zurück, die den
> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'
In der folgenden Tabelle sind alle Ergebnisse des Typs:
typeof null Returning
Ein Beispiel sieht so aus:
value instanceof Constr
Es gibt True zurück, wenn der Wert ein Objekt ist, das vom Konstruktor Constr erstellt wurde (siehe Konstruktoren: Fabriken für Objekte).
> 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
false
Der primitive Boolean-Typ umfasst die Werte true und false.
Wenn JavaScript einen Boolean-Wert erwartet (z. B. für die Bedingung einer if-Anweisung), kann jeder Wert verwendet werden. Er wird entweder als wahr oder falsch interpretiert. Die folgenden Werte werden als falsch interpretiert:
Alle anderen Werte (einschließlich aller Objekte!) gelten als wahr. Werte, die als falsch interpretiert werden, werden als falsch und Werte, die als wahr interpretiert werden, als wahr bezeichnet.
> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
true
Binäre logische Operatoren in JavaScript sind Kurzschluss. Das heißt, wenn der erste Operand ausreicht, um das Ergebnis zu bestimmen, wird der zweite Operand nicht ausgewertet. Zum Beispiel wird in den folgenden Ausdrücken die Funktion foo() nie 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.
Wenn der erste Operand falsch ist, geben Sie ihn zurück. Ansonsten geben Sie den zweiten Operand zurück:
> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'
Wenn der erste Operand wahr ist, geben Sie ihn zurück. Ansonsten geben Sie den zweiten Operand zurück:
> 'abc' || 123
'abc'
> '' || 123
123
JavaScript hat zwei Arten von Gleichheit:
Normalgleichheit betrachtet (zu) viele Werte als gleich (die Details werden in Normal (Lenient) Equality (==,!=) erläutert), was Fehler verbergen kann. Daher wird immer strenge Gleichheit empfohlen.
Alle Zahlen in JavaScript sind mit einem schwimmenden Komma:
> 1 === 1.0
true
Zu den Sondernummern gehören:
NaN (
> Number('xyz') // 'xyz' can’t be converted to a number
NaN
Unendlich Auch meistens ein Fehlerwert:
> 3 / 0
Infinity
> Math.pow(2, 1024) // number too large
Infinity
Unendlichkeit ist größer als jede andere Zahl (außer NaN). Ähnlich ist -Unendlichkeit kleiner als jede andere Zahl (außer NaN). Das macht diese Zahlen als Standardwerte nützlich (z. B. wenn Sie nach einem Minimum oder Maximum suchen).
JavaScript hat folgende arithmetische Operatoren (siehe Arithmetische Operatoren):
Das globale Objekt Mathematik (siehe Mathematik) bietet mehr arithmetische Operationen über Funktionen.
JavaScript hat auch Operatoren für bitwise Operationen (z. B. bitwise And; siehe Bitwise Operatoren).
Strings können direkt über String-Literals erstellt werden. Diese Literals werden durch einzelne oder doppelte Anführungszeichen begrenzt. Der Backslash () entkommt Zeichen und erzeugt einige Kontrollzeichen. 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: \\'
Einzelzeichen werden über eckige Klammern zugegriffen:
> var str = 'abc';
> str[1]
'b'
Die Eigenschaft length zählt die Anzahl der Zeichen in der Zeichenfolge:
> 'abc'.length
3
Wie alle Primitiven sind Zeichenfolgen unveränderlich; Sie müssen eine neue Zeichenfolge erstellen, wenn Sie eine bestehende ändern möchten.
Strings werden über den Plus (+) -Operator verknüpft, der den anderen Operand in eine Zeichenfolge umwandelt, wenn einer der Operanden eine Zeichenfolge ist:
> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'
Um Zeichenfolgen in mehreren Schritten zu verknüpfen, verwenden Sie den +=-Operator:
> var str = '';
> str += 'Multiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Multiple pieces are concatenated.'
Strings haben viele nützliche Methoden (siehe String Prototyp Methoden).
> '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')
-1
In den folgenden Abschnitten werden Bedingungen und Schleifen in JavaScript eingeführt.
Die if-Anweisung hat eine then-Klausel und eine optionale else-Klausel, die abhängig von einer booleanischen Bedingung ausgeführt werden:
if (myvar === 0) {
// then
}
if (myvar === 0) {
// then
} else {
// else
}
if (myvar === 0) {
// then
} else if (myvar === 1) {
// else-if
} else if (myvar === 2) {
// else-if
} else {
// else
}
Ich empfehle, immer Klammern zu verwenden (sie kennzeichnen Blöcke mit null oder mehr Anweisungen).
if (x < 0) return -x;
Der Wert von Frucht entscheidet, welcher Fall ausgeführt wird:
switch (fruit) {
case 'banana':
// ...
break;
case 'apple':
// ...
break;
default: // all other cases
// ...
}
Der
Die For-Schleife hat folgendes Format:
for (⟦«init»⟧; ⟦«condition»⟧; ⟦«post_iteration»⟧)
«statement»
init wird zu Beginn der Schleife ausgeführt. condition wird vor jeder Schleifeiteration geprüft; wenn es falsch wird, wird die Schleife beendet. post_iteration wird nach jeder Schleifeiteration ausgeführt.
Dieses Beispiel druckt alle Elemente des Arrays arr auf der Konsole:
for (var i=0; i < arr.length; i++) {
console.log(arr[i]);
}
Die while-Schleife schleift sich über ihren Körper, solange ihr Zustand besteht:
// Same as for loop above:
var i = 0;
while (i < arr.length) {
console.log(arr[i]);
i++;
}
Da die Bedingung dem Körper folgt, wird der Körper immer mindestens einmal ausgeführt:
do {
// ...
} while (condition);
In allen Schleifen:
Eine Möglichkeit, eine Funktion zu definieren, ist eine Funktionsdeklaration:
function add(param1, param2) {
return param1 + param2;
}
Der vorhergehende Code definiert eine Funktion, add, die zwei Parameter, param1 und param2, hat und gibt die Summe beider Parameter zurück.
> add(6, 1)
7
> add('a', 'b')
'ab'
Eine andere Möglichkeit, add ((() zu definieren, ist, einer Variablen add einen Funktionsausdruck zuzuweisen:
var add = function (param1, param2) {
return param1 + param2;
};
Ein Funktionsausdruck erzeugt einen Wert und kann somit verwendet werden, um Funktionen direkt als Argumente an andere Funktionen zu übergeben:
someOtherFunction(function (p1, p2) { ... });
Funktionsdeklarationen werden vollständig an den Anfang des aktuellen Bereichs gehisst.
function foo() {
bar(); // OK, bar is hoisted
function bar() {
...
}
}
Es ist zu beachten, dass, obwohl auch die Variablen-Deklarationen angehoben werden (siehe Variablen werden angehoben), die von ihnen ausgeführten Aufgaben nicht:
function foo() {
bar(); // Not OK, bar is still undefined
var bar = function () {
// ...
};
}
Sie können jede Funktion in JavaScript mit einer beliebigen Anzahl von Argumenten aufrufen; die Sprache wird sich nie beschweren. Sie wird jedoch alle Parameter über die speziellen Variablenargumente verfügbar machen. argumente 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'
Lassen Sie uns die folgende Funktion verwenden, um zu erforschen, wie zu viele oder zu wenige Parameter in JavaScript behandelt werden (die Funktion toArray( wird in Konvertieren von Argumenten in ein Array gezeigt):
function f(x, y) {
console.log(x, y);
return toArray(arguments);
}
Zusätzliche Parameter werden ignoriert (außer durch Argumente):
> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]
Fehlende Parameter werden den Wert undefiniert:
> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]
Folgendes ist ein übliches Muster für die Zuweisung von Standardwerten an Parameter:
function pair(x, y) {
x = x || 0; // (1)
y = y || 0;
return [ x, y ];
}
In Zeile (1) gibt der Operator x zurück, wenn er wahr ist (nicht null, undefined usw.). Ansonsten gibt er den zweiten Operand zurück:
> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]
Wenn Sie eine Arity (eine bestimmte Anzahl von Parametern) durchsetzen möchten, können Sie arguments.length überprüfen:
function pair(x, y) {
if (arguments.length !== 2) {
throw new Error('Need exactly 2 arguments');
}
...
}
arguments ist kein Array, es ist nur arrayähnlich (siehe Array-like Objects and Generic Methods). Es hat eine Eigenschaftslänge und Sie können auf seine Elemente über Indizes in Quadratklammern zugreifen. Sie können jedoch keine Elemente entfernen oder eine der Array-Methoden darauf aufrufen. Daher müssen Sie manchmal Argumente in ein Array konvertieren, was die folgende Funktion tut (es wird in Array-like Objects und Generic Methods erklärt):
function toArray(arrayLikeObject) {
return Array.prototype.slice.call(arrayLikeObject);
}
Die häufigste Art, mit Ausnahmen umzugehen (siehe Kapitel 14), ist wie folgt:
function getPerson(id) {
if (id < 0) {
throw new Error('ID must not be negative: '+id);
}
return { id: id }; // normally: retrieved from database
}
function getPersons(ids) {
var result = [];
ids.forEach(function (id) {
try {
var person = getPerson(id);
result.push(person);
} catch (exception) {
console.log(exception);
}
});
return result;
}
Die Try-Klausel umringt kritischen Code, und die Catch-Klausel wird ausgeführt, wenn eine Ausnahme in die Try-Klausel geworfen wird.
> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]
Der strenge Modus (siehe Strenge Modus) ermöglicht mehr Warnungen und macht JavaScript zu einer saubereren Sprache (nicht strenger Modus wird manchmal
'use strict';
Sie können auch den strengen Modus pro Funktion aktivieren:
function functionInStrictMode() {
'use strict';
}
In JavaScript deklarieren Sie Variablen über 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:
var x = 1, y = 2, z = 3;
Aber ich empfehle, eine Anweisung pro Variable zu verwenden (der Grund wird in Syntax erklärt).
var x = 1;
var y = 2;
var z = 3;
Wegen des Hebens (siehe Variablen werden gehisst) ist es normalerweise am besten, Variablen zu Beginn einer Funktion zu deklarieren.
Der Umfang einer Variablen ist immer die vollständige Funktion (im Gegensatz zum aktuellen Block).
function foo() {
var x = -512;
if (x < 0) { // (1)
var tmp = -x;
...
}
console.log(tmp); // 512
}
Wir können 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 Variabeldeklaration wird angehoben: Die Erklärung wird an den Anfang der Funktion verschoben, aber die damit verbundenen Aufgaben bleiben gesetzt.
function foo() {
console.log(tmp); // undefined
if (false) {
var tmp = 3; // (1)
}
}
Intern wird die vorhergehende Funktion wie folgt ausgeführt:
function foo() {
var tmp; // hoisted declaration
console.log(tmp);
if (false) {
tmp = 3; // assignment stays put
}
}
Jede Funktion bleibt mit den Variablen der Funktionen, die sie umgeben, verbunden, auch nachdem sie den Umfang verlassen hat, in dem sie erstellt wurde.
function createIncrementor(start) {
return function () { // (1)
start++;
return start;
}
}
Die Funktion, die in Zeile (1) beginnt, verlässt den Kontext, in dem sie erstellt wurde, bleibt aber mit einer Live-Version von start verbunden:
> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8
Eine Schließung ist eine Funktion plus die Verbindung zu den Variablen ihrer umgebenden Bereiche.
Manchmal möchten Sie einen neuen Variablenbereich einführen, um beispielsweise zu verhindern, dass eine Variable global wird. In JavaScript können Sie dafür keinen Block verwenden; Sie müssen eine Funktion verwenden. Aber es gibt ein Muster für die Verwendung einer Funktion in einer blockartigen Weise. Es heißt IIFE (immediately invoked function expression, ausgesprochen
(function () { // open IIFE
var tmp = ...; // not a global variable
}()); // close IIFE
Bitte geben Sie das vorherige Beispiel genau wie gezeigt ein (abgesehen von den Kommentaren). Ein IIFE ist ein Funktionsausdruck, der unmittelbar nach der Definition aufgerufen wird. Innerhalb der Funktion existiert ein neuer Bereich, der verhindert, dass tmp global wird.
Schließungen halten ihre Verbindungen zu äußeren Variablen, was manchmal nicht das ist, was Sie wollen:
var result = [];
for (var i=0; i < 5; i++) {
result.push(function () { return i }); // (1)
}
console.log(result[1]()); // 5 (not 1)
console.log(result[3]()); // 5 (not 3)
Der Wert, der in Zeile (1) zurückgegeben wird, ist immer der aktuelle Wert von i, nicht der Wert, den er 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 einen IIFE verwenden:
for (var i=0; i < 5; i++) {
(function () {
var i2 = i; // copy current i
result.push(function () { return i2 });
}());
}
Dieser Abschnitt behandelt zwei grundlegende objektorientierte Mechanismen von JavaScript: Einzelobjekte und Konstruktoren (die Fabriken für Objekte sind, ähnlich wie Klassen in anderen Sprachen).
Wie alle Werte haben auch Objekte Eigenschaften. Man könnte tatsächlich ein Objekt als eine Reihe von Eigenschaften betrachten, wobei jede Eigenschaft ein (Schlüssel, Wert) Paar ist. Der Schlüssel ist eine Zeichenfolge und der Wert ist ein beliebiger JavaScript-Wert.
In JavaScript können Sie einfache Objekte direkt über Objektliteralen erstellen:
'use strict';
var jane = {
name: 'Jane',
describe: function () {
return 'Person named '+this.name;
}
};
Das vorhergehende Objekt hat die Eigenschaften name und describe.
> jane.name // get
'Jane'
> jane.name = 'John'; // set
> jane.newProperty = 'abc'; // property created automatically
Funktionswerte Eigenschaften wie describe werden Methoden genannt. Sie verwenden dies, um sich auf das Objekt zu beziehen, das verwendet wurde, um sie zu rufen:
> jane.describe() // call method
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'
Der in-Operator prüft, ob eine Eigenschaft existiert:
> 'newProperty' in jane
true
> 'foo' in jane
false
Wenn Sie eine Eigenschaft lesen, die nicht existiert, erhalten Sie den Wert undefined. Daher könnten die beiden vorherigen Prüfungen auch so durchgeführt werden:
> jane.newProperty !== undefined
true
> jane.foo !== undefined
false
Der Delete Operator entfernt eine Eigenschaft:
> delete jane.newProperty
true
> 'newProperty' in jane
false
Ein Eigenschaftsschlüssel kann eine beliebige Zeichenfolge sein. Bisher haben wir Eigenschaftsschlüssel in Objektliteralen und nach dem Punktoperator gesehen. Sie können sie jedoch nur so verwenden, wenn sie Identifikatoren sind (siehe Identifikatoren und Variablennamen). Wenn Sie andere Zeichenfolgen als Schlüssel verwenden möchten, müssen Sie sie in einem Objektliteralen zitieren und quadratische Klammern verwenden, um die Eigenschaft zu erhalten und festzulegen:
> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;
Quadratische Klammern erlauben 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 mit dem Objekt. Alleine ist die Funktion keine Methode mehr, und dies hat den Wert undefined (im strengen Modus).
Als Beispiel, lassen Sie uns zu dem früheren Objekt Jane zurückgehen:
'use strict';
var jane = {
name: 'Jane',
describe: function () {
return 'Person named '+this.name;
}
};
Wir wollen die Methode describe aus jane extrahieren, in eine Variable funk einfügen und aufrufen.
> 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.
> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'
Jede Funktion hat ihre eigene spezielle Variable this. Dies ist unangenehm, wenn Sie eine Funktion in einer Methode nisten, da Sie nicht von der Funktion aus auf die Methode
var jane = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
logHiToFriends: function () {
'use strict';
this.friends.forEach(function (friend) {
// `this` is undefined here
console.log(this.name+' says hi to '+friend);
});
}
}
Aufrufen von logHiToFriends erzeugt einen Fehler:
> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined
Lassen Sie uns zwei Möglichkeiten betrachten, dies zu beheben.
logHiToFriends: function () {
'use strict';
var that = this;
this.friends.forEach(function (friend) {
console.log(that.name+' says hi to '+friend);
});
}
Oder, forEach hat einen zweiten Parameter, der es Ihnen erlaubt, einen Wert dafür bereitzustellen:
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 Funktionsanrufen in JavaScript verwendet.
Bis jetzt haben Sie vielleicht gedacht, dass JavaScript-Objekte nur Karten von Zeichenfolgen zu Werten sind, eine Vorstellung, die von JavaScript
Neben
// Set up instance data
function Point(x, y) {
this.x = x;
this.y = y;
}
// Methods
Point.prototype.dist = function () {
return Math.sqrt(this.x*this.x + this.y*this.y);
};
Wir können sehen, dass ein Konstruktor zwei Teile hat. Erstens setzt die Funktion Point die Instanzdaten ein. Zweitens enthält die Eigenschaft Point.prototype ein Objekt mit den Methoden. Die ersteren Daten sind für jede Instanz spezifisch, während die letzteren Daten unter allen Instanzen geteilt werden.
Um Point zu verwenden, rufen wir ihn über den neuen Operator 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 man über ganzzahlige Indizes ab Null zugreifen kann.
Array-Literals sind nützlich, um Arrays zu erstellen:
> var arr = [ 'a', 'b', 'c' ];
Das vorhergehende Array besteht aus drei Elementen: den Zeichenfolgen
> arr[0]
'a'
> arr[0] = 'x';
> arr
[ 'x', 'b', 'c' ]
Die length-Eigenschaft gibt an, wie viele Elemente ein Array hat.
> var arr = ['a', 'b'];
> arr.length
2
> arr[arr.length] = 'c';
> arr
[ 'a', 'b', 'c' ]
> arr.length
3
> arr.length = 1;
> arr
[ 'a' ]
Der in-Operator 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 Prototyp Methoden). 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 für die Iteration über Elemente (siehe Iteration (Nondestructive)). 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 folgende Ausgabe:
0. a
1. b
2. c
Beachten Sie, dass die Funktion in Zeile (1) frei ist, Argumente zu ignorieren.
map erstellt ein neues Array, indem es jedes Element eines vorhandenen Arrays mit einer Funktion bedient:
> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]
JavaScript unterstützt regelmäßige Ausdrücke, die mit Schrägstücken begrenzt sind:
/^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 komplette Übereinstimmung bei Index 0, die Erfassung der ersten Gruppe bei Index 1 und so weiter.
> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'
Der erste Parameter von replace muss ein regulärer Ausdruck mit einem /g-Flag sein; ansonsten wird nur das erste Vorkommen ersetzt.
Mathematik ist ein Objekt mit arithmetischen Funktionen.
> 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