typeof und instanceof: Was ist der Typ eines Wertes?typeofinstanceofIn diesem Kapitel untersuchen wir, welche Arten von Werten JavaScript hat.
Unterstützendes Werkzeug:
===
In diesem Kapitel werden wir gelegentlich den strikten Gleichheitsoperator verwenden. a === b ergibt true, wenn a und b gleich sind. Was genau das bedeutet, wird in §13.4.2 „Strikte Gleichheit (=== und !==)” erklärt.
Für dieses Kapitel betrachte ich Typen als Mengen von Werten – beispielsweise ist der Typ boolean die Menge { false, true }.
Abb. 6 zeigt die Typenhierarchie von JavaScript. Was lernen wir aus diesem Diagramm?
Object. Jede Instanz von Object ist auch ein Objekt, aber nicht umgekehrt. Fast alle Objekte, denen Sie in der Praxis begegnen werden, sind jedoch Instanzen von Object – zum Beispiel Objekte, die über Objektliterale erstellt wurden. Weitere Details zu diesem Thema finden Sie in §29.7.3 „Nicht alle Objekte sind Instanzen von Object“.Die ECMAScript-Spezifikation kennt nur insgesamt acht Typen. Die Namen dieser Typen sind (ich verwende die Namen von TypeScript, nicht die Namen der Spezifikation)
undefined mit dem einzigen Element undefinednull mit dem einzigen Element nullboolean mit den Elementen false und truenumber der Typ aller Zahlen (z.B. -123, 3.141)bigint der Typ aller großen ganzen Zahlen (z.B. -123n)string der Typ aller Zeichenketten (z.B. 'abc')symbol der Typ aller Symbole (z.B. Symbol('Mein Symbol'))object der Typ aller Objekte (unterschiedlich von Object, dem Typ aller Instanzen der Klasse Object und ihrer Unterklassen)Die Spezifikation macht eine wichtige Unterscheidung zwischen Werten
undefined, null, boolean, number, bigint, string, symbol.Im Gegensatz zu Java (das JavaScript hier inspiriert hat) sind primitive Werte keine Bürger zweiter Klasse. Der Unterschied zwischen ihnen und Objekten ist subtiler. Kurz gesagt
Abgesehen davon sind primitive Werte und Objekte ziemlich ähnlich: Sie haben beide Eigenschaften (Schlüssel-Wert-Einträge) und können an denselben Stellen verwendet werden.
Als Nächstes werden wir primitive Werte und Objekte eingehender betrachten.
Sie können keine Eigenschaften von Primitiven ändern, hinzufügen oder entfernen
const str = 'abc';
assert.equal(str.length, 3);
assert.throws(
() => { str.length = 1 },
/^TypeError: Cannot assign to read only property 'length'/
);Primitive werden per Wert übergeben: Variablen (einschließlich Parameter) speichern die Inhalte der Primitiven. Wenn ein primitiver Wert einer Variablen zugewiesen oder als Argument an eine Funktion übergeben wird, wird sein Inhalt kopiert.
const x = 123;
const y = x;
// `y` is the same as any other number 123
assert.equal(y, 123); Beobachtung des Unterschieds zwischen Übergabe per Wert und Übergabe per Referenz
Aufgrund der Tatsache, dass primitive Werte unveränderlich sind und nach Wert verglichen werden (siehe nächster Unterabschnitt), gibt es keine Möglichkeit, den Unterschied zwischen Übergabe per Wert und Übergabe per Identität (wie sie für Objekte in JavaScript verwendet wird) zu beobachten.
Primitive werden nach Wert verglichen: Beim Vergleichen zweier primitiver Werte vergleichen wir ihre Inhalte.
assert.equal(123 === 123, true);
assert.equal('abc' === 'abc', true);Um zu sehen, was an dieser Vergleichsweise so besonders ist, lesen Sie weiter und erfahren Sie, wie Objekte verglichen werden.
Objekte werden ausführlich in §28 „Objekte“ und im folgenden Kapitel behandelt. Hier konzentrieren wir uns hauptsächlich darauf, wie sie sich von primitiven Werten unterscheiden.
Lassen Sie uns zuerst zwei gängige Möglichkeiten zur Erstellung von Objekten untersuchen
Objektliteral
const obj = {
first: 'Jane',
last: 'Doe',
};Das Objektliteral beginnt und endet mit geschweiften Klammern {}. Es erstellt ein Objekt mit zwei Eigenschaften. Die erste Eigenschaft hat den Schlüssel 'first' (eine Zeichenkette) und den Wert 'Jane'. Die zweite Eigenschaft hat den Schlüssel 'last' und den Wert 'Doe'. Für weitere Informationen zu Objektliteralen konsultieren Sie §28.3.1 „Objektliterale: Eigenschaften“.
Arrayliteral
const fruits = ['strawberry', 'apple'];Das Arrayliteral beginnt und endet mit eckigen Klammern []. Es erstellt ein Array mit zwei Elementen: 'strawberry' und 'apple'. Für weitere Informationen zu Arrayliteralen konsultieren Sie §31.3.1 „Arrays erstellen, lesen, schreiben“.
Standardmäßig können Sie die Eigenschaften von Objekten frei ändern, hinzufügen und entfernen
const obj = {};
obj.count = 2; // add a property
assert.equal(obj.count, 2);
obj.count = 3; // change a property
assert.equal(obj.count, 3);Objekte werden per Identität übergeben (mein Begriff): Variablen (einschließlich Parameter) speichern die Identitäten von Objekten.
Die Identität eines Objekts ist wie ein Zeiger (oder eine transparente Referenz) auf die tatsächlichen Daten des Objekts im Heap (denken Sie an den gemeinsamen Hauptspeicher einer JavaScript-Engine).
Beim Zuweisen eines Objekts zu einer Variablen oder beim Übergeben als Argument an eine Funktion wird seine Identität kopiert. Jedes Objektliteral erstellt ein neues Objekt im Heap und gibt seine Identität zurück.
const a = {}; // fresh empty object
// Pass the identity in `a` to `b`:
const b = a;
// Now `a` and `b` point to the same object
// (they “share” that object):
assert.equal(a === b, true);
// Changing `a` also changes `b`:
a.name = 'Tessa';
assert.equal(b.name, 'Tessa');JavaScript verwendet Garbage Collection zur automatischen Speicherverwaltung
let obj = { prop: 'value' };
obj = {};Nun ist der alte Wert { prop: 'value' } von obj Müll (wird nicht mehr verwendet). JavaScript wird ihn automatisch garbage-collecten (aus dem Speicher entfernen), zu einem bestimmten Zeitpunkt (möglicherweise nie, wenn genügend freier Speicher vorhanden ist).
Details: Übergabe per Identität
„Übergabe per Identität“ bedeutet, dass die Identität eines Objekts (eine transparente Referenz) per Wert übergeben wird. Dieser Ansatz wird auch als „Übergabe durch Teilen“ bezeichnet.
Objekte werden nach Identität verglichen (mein Begriff): Zwei Variablen sind nur dann gleich, wenn sie die gleiche Objektidentität enthalten. Sie sind nicht gleich, wenn sie auf unterschiedliche Objekte mit demselben Inhalt verweisen.
const obj = {}; // fresh empty object
assert.equal(obj === obj, true); // same identity
assert.equal({} === {}, false); // different identities, same contenttypeof und instanceof: Was ist der Typ eines Wertes?Die beiden Operatoren typeof und instanceof ermöglichen es Ihnen festzustellen, welchen Typ ein gegebener Wert x hat
if (typeof x === 'string') ···
if (x instanceof Array) ···Wie unterscheiden sie sich?
typeof unterscheidet die 7 Typen der Spezifikation (minus eine Auslassung, plus eine Ergänzung).instanceof testet, welche Klasse einen gegebenen Wert erzeugt hat. Faustregel:
typeof ist für primitive Werte; instanceof ist für Objekte
typeofx |
typeof x |
|---|---|
undefined |
'undefined' |
null |
'object' |
| Boolean | 'boolean' |
| Number | 'number' |
| Bigint | 'bigint' |
| String | 'string' |
| Symbol | 'symbol' |
| Funktion | 'function' |
| Alle anderen Objekte | 'object' |
Tabelle 2 listet alle Ergebnisse von typeof auf. Sie entsprechen ungefähr den 7 Typen der Sprachspezifikation. Leider gibt es zwei Unterschiede, und das sind Sprachquirks
typeof null gibt 'object' zurück und nicht 'null'. Das ist ein Fehler. Leider kann er nicht behoben werden. TC39 hat versucht, das zu tun, aber es hat zu viel Code im Web kaputt gemacht.typeof einer Funktion sollte 'object' sein (Funktionen sind Objekte). Die Einführung einer separaten Kategorie für Funktionen ist verwirrend.Dies sind einige Beispiele für die Verwendung von typeof
> typeof undefined
'undefined'
> typeof 123n
'bigint'
> typeof 'abc'
'string'
> typeof {}
'object' Übungen: Zwei Übungen zu
typeof
exercises/values/typeof_exrc.mjs
exercises/values/is_object_test.mjsinstanceofDieser Operator beantwortet die Frage: Wurde ein Wert x von einer Klasse C erzeugt?
x instanceof CZum Beispiel
> (function() {}) instanceof Function
true
> ({}) instanceof Object
true
> [] instanceof Array
truePrimitive Werte sind keine Instanzen von irgendetwas
> 123 instanceof Number
false
> '' instanceof String
false
> '' instanceof Object
false Übung:
instanceof
exercises/values/instanceof_exrc.mjs
JavaScripts ursprüngliche Fabriken für Objekte sind Konstruktorfunktionen: gewöhnliche Funktionen, die „Instanzen“ von sich selbst zurückgeben, wenn sie mit dem Operator new aufgerufen werden.
ES6 führte Klassen ein, die hauptsächlich eine bessere Syntax für Konstruktorfunktionen sind.
In diesem Buch verwende ich die Begriffe Konstruktorfunktion und Klasse austauschbar.
Klassen können als eine Partitionierung des einzelnen Typs object der Spezifikation in Untertypen betrachtet werden – sie geben uns mehr Typen als die begrenzten 7 Typen der Spezifikation. Jede Klasse ist der Typ der Objekte, die von ihr erstellt wurden.
Jeder primitive Typ (außer den internen Typen der Spezifikation für undefined und null) hat eine zugeordnete Konstruktorfunktion (denken Sie an Klasse)
Boolean ist mit Boole'schen Werten assoziiert.Number ist mit Zahlen assoziiert.String ist mit Zeichenketten assoziiert.Symbol ist mit Symbolen assoziiert.Jede dieser Funktionen spielt mehrere Rollen – zum Beispiel Number
Sie können sie als Funktion verwenden und Werte in Zahlen konvertieren
assert.equal(Number('123'), 123);Number.prototype liefert die Eigenschaften für Zahlen – zum Beispiel die Methode .toString()
assert.equal((123).toString, Number.prototype.toString);Number ist ein Namespace/Container-Objekt für Tool-Funktionen für Zahlen – zum Beispiel
assert.equal(Number.isInteger(123), true);Schließlich können Sie Number auch als Klasse verwenden und Zahlenobjekte erstellen. Diese Objekte unterscheiden sich von echten Zahlen und sollten vermieden werden.
assert.notEqual(new Number(123), 123);
assert.equal(new Number(123).valueOf(), 123);Die Konstruktorfunktionen, die mit primitiven Typen zusammenhängen, werden auch als Wrapper-Typen bezeichnet, da sie den kanonischen Weg zur Konvertierung primitiver Werte in Objekte bieten. Dabei werden primitive Werte in Objekte „gewrappt“.
const prim = true;
assert.equal(typeof prim, 'boolean');
assert.equal(prim instanceof Boolean, false);
const wrapped = Object(prim);
assert.equal(typeof wrapped, 'object');
assert.equal(wrapped instanceof Boolean, true);
assert.equal(wrapped.valueOf(), prim); // unwrapDas Wrapping spielt in der Praxis selten eine Rolle, wird aber intern in der Sprachspezifikation verwendet, um Primitiven Eigenschaften zu geben.
Es gibt zwei Möglichkeiten, wie Werte in JavaScript in andere Typen konvertiert werden
String().Die Funktion, die mit einem primitiven Typ assoziiert ist, konvertiert Werte explizit in diesen Typ
> Boolean(0)
false
> Number('123')
123
> String(123)
'123'Sie können auch Object() verwenden, um Werte in Objekte zu konvertieren
> typeof Object(123)
'object'Die folgende Tabelle beschreibt detaillierter, wie diese Konvertierung funktioniert
x |
Object(x) |
|---|---|
undefined |
{} |
null |
{} |
| boolean | new Boolean(x) |
| number | new Number(x) |
| bigint | Eine Instanz von BigInt (new wirft TypeError) |
| string | new String(x) |
| symbol | Eine Instanz von Symbol (new wirft TypeError) |
| object | x |
Bei vielen Operationen konvertiert JavaScript die Operanden/Parameter automatisch, wenn ihre Typen nicht passen. Diese Art der automatischen Konvertierung wird Coercion genannt.
Zum Beispiel konvertiert der Multiplikationsoperator seine Operanden in Zahlen
> '7' * '3'
21Viele integrierte Funktionen führen ebenfalls Coercion durch. Zum Beispiel konvertiert Number.parseInt() seinen Parameter vor dem Parsen in eine Zeichenkette. Das erklärt folgendes Ergebnis
> Number.parseInt(123.45)
123Die Zahl 123.45 wird in die Zeichenkette '123.45' konvertiert, bevor sie geparst wird. Das Parsen stoppt vor dem ersten Nicht-Ziffern-Zeichen, weshalb das Ergebnis 123 ist.
Übung: Werte in Primitive konvertieren
exercises/values/conversion_exrc.mjs
Quiz
Siehe Quiz-App.