JavaScript hat die meisten der Werte, die wir von Programmiersprachen erwarten: Booleans, Zahlen, Zeichenketten, Arrays und so weiter. Alle normalen Werte in JavaScript haben Eigenschaften.[9] 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 auf Eigenschaften zuzugreifen:
> var obj = {}; // create an empty object
> obj.foo = 123; // write property
123
> obj.foo // read property
123
> 'abc'.toUpperCase() // call method
'ABC'Dieses Kapitel gibt einen Überblick über das Typsystem von JavaScript.
JavaScript hat nur sechs Typen, gemäß Kapitel 8 der ECMAScript-Sprachspezifikation
Ein ECMAScript-Sprachtyp entspricht Werten, die von einem ECMAScript-Programmierer direkt mit der ECMAScript-Sprache manipuliert werden. Die ECMAScript-Sprachtypen sind
- Undefined, Null
- Boolean, String, Number und
- Object
Daher führen Konstruktoren technisch keine neuen Typen ein, obwohl gesagt wird, dass sie Instanzen haben.
In einer statisch typisierten Sprache haben Variablen, Parameter und Objekteigenschaften (JavaScript nennt sie Eigenschaften) Typen, die dem Compiler zur Kompilierzeit bekannt sind. Der Compiler kann diese Informationen verwenden, um Typüberprüfungen durchzuführen und den kompilierten Code zu optimieren.
Auch in statisch typisierten Sprachen hat eine Variable auch einen dynamischen Typ, den Typ des Wertes der Variablen zu einem bestimmten Zeitpunkt zur Laufzeit. Der dynamische Typ kann vom statischen Typ abweichen. Zum Beispiel (Java)
Objectfoo="abc";
Der statische Typ von foo ist Object; sein dynamischer Typ ist String.
JavaScript ist dynamisch typisiert; Typen von Variablen sind zur Kompilierzeit im Allgemeinen nicht bekannt.
JavaScript führt eine sehr begrenzte Art der dynamischen Typprüfung durch
> var foo = null; > foo.prop TypeError: Cannot read property 'prop' of null
Meistens schlagen die Dinge jedoch stillschweigend fehl oder funktionieren. Wenn Sie zum Beispiel auf eine nicht vorhandene Eigenschaft zugreifen, erhalten Sie den Wert undefined
> var bar = {};
> bar.prop
undefinedIn JavaScript ist der hauptsächliche Weg, mit einem Wert umzugehen, dessen Typ nicht passt, ihn in den richtigen Typ zu coercen. Coercion bedeutet implizite Typkonvertierung. Die meisten Operanden werden coercet:
> '3' * '4' 12
Die integrierten Konvertierungsmechanismen von JavaScript unterstützen nur die Typen Boolean, Number, String und Object. Es gibt keine standardmäßige Möglichkeit, eine Instanz eines Konstruktors in eine Instanz eines anderen Konstruktors zu konvertieren.
Die Begriffe stark typisiert und schwach typisiert haben keine allgemein aussagekräftigen Definitionen. Sie werden verwendet, aber normalerweise falsch. Es ist besser, stattdessen statisch typisiert, statisch typprüft usw. zu verwenden.
JavaScript trifft eine etwas willkürliche Unterscheidung zwischen Werten:
null und undefined.Ein Hauptunterschied zwischen den beiden ist, 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
> var obj3 = obj1;
> obj3 === obj1
trueIm Gegensatz dazu werden alle primitiven Werte, die denselben Wert kodieren, als gleich betrachtet
> var prim1 = 123; > var prim2 = 123; > prim1 === prim2 true
Die folgenden beiden Abschnitte erläutern primitive Werte und Objekte im Detail.
Die folgenden sind alle primitiven Werte (Primitives kurz):
true, false (siehe Kapitel 10)1736, 1.351 (siehe Kapitel 11)'abc', "abc" (siehe Kapitel 12)undefined, null (siehe undefined und null)Primitives haben die folgenden Charakteristiken:
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:
Normale Objekte (Konstruktor Object) können durch Objektliterale erstellt werden (siehe Kapitel 17)
{firstName:'Jane',lastName:'Doe'}
Das vorhergehende Objekt hat zwei Eigenschaften: der Wert der Eigenschaft firstName ist 'Jane' und der Wert der Eigenschaft lastName ist 'Doe'.
Arrays (Konstruktor Array) können durch Arrayliterale erstellt werden (siehe Kapitel 18)
['apple','banana','cherry']
Das vorhergehende Array hat drei Elemente, die über numerische Indizes aufgerufen werden können. Zum Beispiel ist der Index von 'apple' 0.
Reguläre Ausdrücke (Konstruktor RegExp) können durch reguläre Ausdruckliterale erstellt werden (siehe Kapitel 19)
/^a+b+$/Objekte haben die folgenden Charakteristiken
Identitäten werden verglichen; jedes Objekt 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 Dot Operator (.): Accessing Properties via Fixed Keys)
> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123JavaScript hat zwei „Nichtwerte“, die fehlende Informationen anzeigen, undefined und null:
undefined bedeutet „kein Wert“ (weder primitiv noch Objekt). Uninitialisierte Variablen, fehlende Parameter und fehlende Eigenschaften haben diesen Nichtwert. Und Funktionen geben ihn implizit zurück, wenn nichts explizit zurückgegeben wurde. null bedeutet „kein Objekt“. Es wird als Nichtwert verwendet, wo ein Objekt erwartet wird (als Parameter, als Mitglied in einer Kette von Objekten usw.). undefined und null sind die einzigen Werte, für die jede Art von Eigenschaftszugriff zu einer Ausnahme führt
> function returnFoo(x) { return x.foo }
> returnFoo(true)
undefined
> returnFoo(0)
undefined
> returnFoo(null)
TypeError: Cannot read property 'foo' of null
> returnFoo(undefined)
TypeError: Cannot read property 'foo' of undefinedundefined wird auch manchmal als Metawert verwendet, der Nichtexistenz anzeigt. null hingegen zeigt Leere an. Zum Beispiel gibt ein JSON-Knotenbesucher (siehe Transforming Data via Node Visitors)
undefined, um eine Objekteigenschaft oder ein Arrayelement zu entfernennull, um die Eigenschaft oder das Element auf null zu setzenHier überprüfen wir die verschiedenen Szenarien, in denen undefined und null vorkommen.
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
undefinedUnd Funktionen geben implizit undefined zurück, wenn nichts explizit zurückgegeben wurde:
> function f() {}
> f()
undefined
> function g() { return; }
> g()
undefined
null ist das letzte Element in der Prototypenkette (eine Kette von Objekten; siehe Layer 2: The Prototype Relationship Between Objects)
> Object.getPrototypeOf(Object.prototype) null
null wird von RegExp.prototype.exec() zurückgegeben, wenn es keine Übereinstimmung für den regulären Ausdruck in der Zeichenkette gab
> /x/.exec('aaa')
nullIn den folgenden Abschnitten überprüfen wir, wie man individuell auf undefined und null prüft oder ob eines von beiden existiert.
Strikte Gleichheit (===) ist der kanonische Weg, um auf undefined zu prüfen:
if(x===undefined)...
Sie können auch auf undefined über den typeof-Operator prüfen (typeof: Categorizing Primitives), aber Sie sollten normalerweise den oben genannten Ansatz verwenden.
Die meisten Funktionen erlauben Ihnen, einen fehlenden Wert entweder über undefined oder null anzugeben. Eine Möglichkeit, beide zu prüfen, ist über einen expliziten Vergleich:
// Does x have a value?if(x!==undefined&&x!==null){...}// Is x a non-value?if(x===undefined||x===null){...}
Eine andere Möglichkeit ist, die Tatsache auszunutzen, dass sowohl undefined als auch null als false gelten (siehe Truthy and Falsy Values)
// Does x have a value (is it truthy)?if(x){...}// Is x falsy?if(!x){...}
false, 0, NaN und '' gelten ebenfalls als false.
Ein einziger Nichtwert könnte die Rollen von sowohl undefined als auch null spielen. Warum hat JavaScript zwei solche Werte? Der Grund ist historisch bedingt.
JavaScript übernahm den Ansatz von Java, Werte in Primitives und Objekte zu unterteilen. Es verwendete auch den Wert von Java für „kein Objekt“, null. Nach dem Vorbild von C (aber nicht Java) wird null zu 0, wenn es zu einer Zahl coercet wird:
> Number(null) 0 > 5 + null 5
Denken Sie daran, dass die erste Version von JavaScript keine Ausnahmebehandlung hatte. Daher mussten Ausnahmefälle wie uninitialisierte Variablen und fehlende Eigenschaften über einen Wert angezeigt werden. null wäre eine gute Wahl gewesen, aber Brendan Eich wollte zu dieser Zeit zwei Dinge vermeiden
Infolgedessen fügte Eich undefined als zusätzlichen Nichtwert zur Sprache hinzu. Es coercet zu NaN
> Number(undefined) NaN > 5 + undefined NaN
undefined ist eine Eigenschaft des globalen Objekts (und somit eine globale Variable; siehe The Global Object). Unter ECMAScript 3 mussten Sie Vorkehrungen beim Lesen von undefined treffen, da es leicht war, seinen Wert versehentlich zu ändern. Unter ECMAScript 5 ist das nicht notwendig, da undefined schreibgeschützt ist.
Um gegen ein geändertes undefined zu schützen, waren zwei Techniken beliebt (sie sind für ältere JavaScript-Engines immer noch relevant)
Überschatten Sie das globale undefined (das möglicherweise den falschen Wert hat)
(function(undefined){if(x===undefined)...// safe now}());// don’t hand in a parameter
Im obigen Code hat undefined garantiert den richtigen Wert, da es sich um einen Parameter handelt, dessen Wert nicht durch den Funktionsaufruf bereitgestellt wurde.
Vergleichen Sie mit void 0, was immer (das richtige) undefined ist (siehe The void Operator)
if(x===void0)// always safe
Die drei primitiven Typen boolean, number und string haben entsprechende Konstruktoren: Boolean, Number, String. Ihre Instanzen (sogenannte Wrapper-Objekte) enthalten (wrap) primitive Werte. Die Konstruktoren können auf zwei Arten verwendet werden:
Als Konstruktoren erstellen sie Objekte, die weitgehend inkompatibel mit den primitiven Werten sind, die sie wrappen
> typeof new String('abc')
'object'
> new String('abc') === 'abc'
falseAls Funktionen konvertieren sie Werte in die entsprechenden primitiven Typen (siehe Functions for Converting to Boolean, Number, String, and Object). Dies ist die empfohlene Methode der Konvertierung
> String(123) '123'
Es wird als bewährte Praxis angesehen, Wrapper-Objekte zu vermeiden. Sie benötigen sie normalerweise nicht, da es nichts gibt, was Objekte tun können, was Primitives nicht können (mit Ausnahme der Veränderbarkeit). (Dies unterscheidet sich von Java, von dem JavaScript den Unterschied zwischen Primitives und Objekten geerbt hat!)
Primitive Werte wie 'abc' unterscheiden sich grundlegend von Wrapper-Instanzen wie new String('abc'):
> typeof 'abc' // a primitive value
'string'
> typeof new String('abc') // an object
'object'
> 'abc' instanceof String // never true for primitives
false
> 'abc' === new String('abc')
falseWrapper-Instanzen sind Objekte, und es gibt keine Möglichkeit, Objekte in JavaScript zu vergleichen, nicht einmal über den nachgiebigen Gleichheitsoperator == (siehe Equality Operators: === Versus ==)
> var a = new String('abc');
> var b = new String('abc');
> a == b
falseWrappen Sie ein Primitive, indem Sie einen Wrapper-Konstruktor aufrufen
newBoolean(true)newNumber(123)newString('abc')
Entpacken Sie ein Primitive durch Aufrufen der Methode valueOf(). Alle Objekte haben diese Methode (wie in Conversion to Primitive) besprochen
> new Boolean(true).valueOf()
true
> new Number(123).valueOf()
123
> new String('abc').valueOf()
'abc'Das richtige Konvertieren von Wrapper-Objekten in Primitives extrahiert Zahlen und Zeichenketten, aber keine Booleans:
> Boolean(new Boolean(false)) // does not unwrap
true
> Number(new Number(123)) // unwraps
123
> String(new String('abc')) // unwraps
'abc'Der Grund dafür wird in Converting to Boolean erklärt.
Primitives haben keine eigenen Methoden und leihen sie sich von Wrappern
>'abc'.charAt===String.prototype.charAttrue
Sloppy Mode und Strict Mode behandeln dieses Ausleihen unterschiedlich. Im Sloppy Mode werden Primitives on the fly in Wrapper konvertiert
String.prototype.sloppyMethod=function(){console.log(typeofthis);// objectconsole.log(thisinstanceofString);// true};''.sloppyMethod();// call the above method
Im Strict Mode werden Methoden vom Wrapper-Prototyp transparent verwendet
String.prototype.strictMethod=function(){'use strict';console.log(typeofthis);// stringconsole.log(thisinstanceofString);// false};''.strictMethod();// call the above method
Typumwandlung bedeutetdie implizite Konvertierung eines Wertes eines Typs in einen Wert eines anderen Typs. Die meisten JavaScript-Operatoren, -Funktionen und -Methoden wandeln Operanden und Argumente in die Typen um, die sie benötigen. Zum Beispiel werden die Operanden des Multiplikationsoperators (*) in Zahlen umgewandelt:
> '3' * '4' 12
Als weiteres Beispiel, wenn einer der Operanden eine Zeichenkette ist, konvertiert der Plus-Operator (+) den anderen in eine Zeichenkette
> 3 + ' times' '3 times'
Daher beschwert sich JavaScript selten über einen falschen Typ eines Wertes. Programme erhalten zum Beispiel normalerweise Benutzereingaben (von Online-Formularen oder GUI-Widgets) als Zeichenketten, auch wenn der Benutzer eine Zahl eingegeben hat. Wenn Sie eine Zahl-als-Zeichenkette wie eine Zahl behandeln, erhalten Sie keine Warnung, nur unerwartete Ergebnisse. Zum Beispiel
varformData={width:'100'};// You think formData.width is a number// and get unexpected resultsvarw=formData.width;varouter=w+20;// You expect outer to be 120, but it’s notconsole.log(outer===120);// falseconsole.log(outer==='10020');// true
In Fällen wie dem vorherigen sollten Sie frühzeitig in den entsprechenden Typ konvertieren
varw=Number(formData.width);
Die folgenden Funktionen sind die bevorzugte Methode, umeinen Wert in einen Boolean, eine Zahl, eine Zeichenkette oder ein Objekt zu konvertieren:
Boolean() (siehe Converting to Boolean)Konvertiert einen Wert in einen Boolean. Die folgenden Werte werden in false konvertiert; dies sind die sogenannten „falsy“-Werte:
undefined, nullfalse
0, NaN''
Alle anderen Werte gelten als „truthy“ und werden in true konvertiert (einschließlich aller Objekte!).
Number() (siehe Converting to Number)Konvertiert einen Wert in eine Zahl:
undefined wird zu NaN.null wird zu 0.false wird zu 0, true wird zu 1.String() (siehe Converting to String)Konvertiert einen Wert in eine Zeichenkette. Er hat offensichtliche Ergebnisse für alle Primitives. Zum Beispiel:
> String(null) 'null' > String(123.45) '123.45' > String(false) 'false'
Objekte werden zuerst in Primitives konvertiert (wird gleich besprochen), die dann in Zeichenketten konvertiert werden.
Object() (siehe Converting Any Value to an Object)Konvertiert Objekte in sich selbst, undefined und null in leere Objekte und Primitives in gewrappte Primitives. Zum Beispiel:
> var obj = { foo: 123 };
> Object(obj) === obj
true
> Object(undefined)
{}
> Object('abc') instanceof String
trueBeachten Sie, dass Boolean(), Number(), String() und Object() als Funktionen aufgerufen werden. Sie verwenden sie normalerweise nicht als Konstruktoren. Dann erstellen sie Instanzen von sich selbst (siehe Wrapper Objects for Primitives).
Um einen Wert in eine Zahl oder eine Zeichenkette zu konvertieren, wird er zuerst in einen beliebigen primitiven Wert konvertiert, der dann in den endgültigen Typ konvertiert wird (wie in Functions for Converting to Boolean, Number, String, and Object) besprochen).
Die ECMAScript-Spezifikation hat eine interne Funktion, ToPrimitive() (die von JavaScript aus nicht zugänglich ist), die diese Konvertierung durchführt. Das Verständnis von ToPrimitive() ermöglicht es Ihnen zu konfigurieren, wie Objekte in Zahlen und Zeichenketten konvertiert werden. Sie hat die folgende Signatur
ToPrimitive(input,PreferredType?)
Der optionale Parameter PreferredType gibt den endgültigen Typ der Konvertierung an: er ist entweder Number oder String, je nachdem, ob das Ergebnis von ToPrimitive() in eine Zahl oder eine Zeichenkette konvertiert wird.
Wenn PreferredType Number ist, führen Sie die folgenden Schritte aus
input primitiv ist, geben Sie ihn zurück (es gibt nichts mehr zu tun).input ein Objekt. Rufen Sie input.valueOf() auf. Wenn das Ergebnis primitiv ist, geben Sie es zurück.input.toString() auf. Wenn das Ergebnis primitiv ist, geben Sie es zurück.TypeError (die das Fehlschlagen der Konvertierung von input in ein Primitiv anzeigt).Wenn PreferredType String ist, werden Schritt 2 und 3 vertauscht. PreferredType kann auch weggelassen werden; es wird dann für Daten als String und für alle anderen Werte als Number betrachtet. Dies ist, wie die Operatoren + und == ToPrimitive() aufrufen.
Die Standardimplementierung von valueOf() gibt this zurück, während die Standardimplementierung von toString() Typinformationen zurückgibt:
> var empty = {};
> empty.valueOf() === empty
true
> empty.toString()
'[object Object]'Daher überspringt Number() valueOf() und konvertiert das Ergebnis von toString() in eine Zahl; das heißt, es konvertiert '[object Object]' in NaN
> Number({})
NaNDas folgende Objekt passt valueOf() an, was Number() beeinflusst, aber nichts für String() ändert
> var n = { valueOf: function () { return 123 } };
> Number(n)
123
> String(n)
'[object Object]'Das folgende Objekt passt toString() an. Da das Ergebnis in eine Zahl konvertiert werden kann, kann Number() eine Zahl zurückgeben
> var s = { toString: function () { return '7'; } };
> String(s)
'7'
> Number(s)
7[9] Technisch gesehen haben primitive Werte keine eigenen Eigenschaften, sie leihen sie von Wrapper-Konstruktoren. Aber das geschieht hinter den Kulissen, so dass man es normalerweise nicht sieht.