Start · Sprachen · JavaScript · Referenz · Symbol.hasInstance

Symbol.hasInstance

Eigenschaft

Bekanntes Symbol, über das ein Konstruktor bzw. Objekt festlegen kann, wie der <code>instanceof</code>-Operator für es ausgewertet wird.

seit JavaScript ES2015 Kategorie: method-property

Signatur

Symbol.hasInstance

Beschreibung

Symbol.hasInstance ist eine statische Dateneigenschaft von Symbol. Sie enthält das bekannte Symbol (engl. well-known symbol) @@hasInstance. Beim Ausdruck x instanceof C sucht die Engine zuerst nach der Methode C[Symbol.hasInstance]. Gibt es sie, ruft die Engine sie mit x als Argument auf und wandelt das Ergebnis in einen booleschen Wert um. Gibt es sie nicht, prüft sie wie üblich die Prototypkette.

Damit lässt sich instanceof anpassen, zum Beispiel für Duck-Typing (ein Objekt gilt als Instanz, wenn es bestimmte Methoden hat), für Typprüfungen über mehrere Realms hinweg (iframes, vm-Kontexte in Node.js) oder für „Typ-Objekte“, die gar keine Konstruktoren sind.

Function.prototype[Symbol.hasInstance] ist die Standardimplementierung, die alle Funktionen erben. Sie ist nicht beschreibbar (writable: false). Eine bloße Zuweisung wie MyClass[Symbol.hasInstance] = fn hat deshalb keine Wirkung. Im Strict Mode wirft sie sogar einen TypeError. Eigene Implementierungen legt man deshalb mit static [Symbol.hasInstance](obj) in einer Klasse oder mit Object.defineProperty an.

Die Eigenschaft selbst ist nicht beschreibbar, nicht aufzählbar und nicht konfigurierbar.

Parameter

Name Typ Default Beschreibung
$instance Pflicht any Beim Aufruf der Methode [Symbol.hasInstance](instance): der linke Operand von instanceof, also der Wert, der geprüft wird.

Rückgabewert

Typ
symbol
Beschreibung
Die Eigenschaft liefert das bekannte Symbol Symbol.hasInstance. Eine Methode, die unter diesem Schlüssel hinterlegt ist, sollte einen booleschen Wert zurückgeben. Andere Rückgabewerte wandelt instanceof mit ToBoolean um.

Beispiele

Duck-Typing mit instanceof

class Iterierbar {
  static [Symbol.hasInstance](obj) {
    return obj != null && typeof obj[Symbol.iterator] === 'function';
  }
}

console.log([1, 2, 3] instanceof Iterierbar);
console.log('abc' instanceof Iterierbar);
console.log(new Map() instanceof Iterierbar);
console.log({} instanceof Iterierbar);
true true true false

Typ-Objekt ohne Konstruktor

const Gerade = {
  [Symbol.hasInstance]: (n) => Number.isInteger(n) && n % 2 === 0
};

const werte = [1, 2, 7, 10, 3.5];
console.log(werte.filter((w) => w instanceof Gerade));
[ 2, 10 ]

Zuweisung vs. Object.defineProperty

'use strict';
function Punkt(x, y) { this.x = x; this.y = y; }

try {
  // schlägt fehl: geerbte Eigenschaft ist nicht beschreibbar
  Punkt[Symbol.hasInstance] = () => true;
} catch (e) {
  console.log(e.constructor.name);
}

Object.defineProperty(Punkt, Symbol.hasInstance, {
  value: (obj) => typeof obj?.x === 'number' && typeof obj?.y === 'number'
});

console.log({ x: 1, y: 2 } instanceof Punkt);
TypeError true

Standardverhalten direkt aufrufen

class Tier {}
class Hund extends Tier {}

const bello = new Hund();
// entspricht bello instanceof Tier
console.log(Tier[Symbol.hasInstance](bello));
console.log(Function.prototype[Symbol.hasInstance].call(Tier, {}));
true false

// Wichtig · Fallstricke

  • Nicht beschreibbar: Function.prototype[Symbol.hasInstance] hat writable: false. Eine einfache Zuweisung an einen Konstruktor überschreibt die Methode deshalb nicht. Im Sloppy Mode passiert dabei still nichts, im Strict Mode wird ein TypeError geworfen. Verwende stattdessen static [Symbol.hasInstance] oder Object.defineProperty.
  • Ist der rechte Operand kein Objekt, wirft instanceof einen TypeError. Dasselbe passiert, wenn er weder eine [Symbol.hasInstance]-Methode hat noch aufrufbar ist.
  • Eine eigene hasInstance-Logik kann dem Verhalten von instanceof widersprechen, das andere Entwickler erwarten, etwa beim Abgleich mit Object.getPrototypeOf. Nutze sie sparsam und dokumentiere sie.
  • Die Methode läuft bei jeder instanceof-Prüfung. Aufwendige Prüfungen in häufig durchlaufenem Code können daher Leistung kosten.
  • Kompatibilität: Unterstützt in allen modernen Browsern (Baseline) und in Node.js ab Version 6.5. Im Internet Explorer gibt es sie nicht, und Transpiler können sie nicht vollständig nachbilden.