JavaScript Prototyp-Methoden ohne __proto__
Erfahren Sie mehr über moderne JavaScript-Prototyp-Methoden wie Object.create(), Object.getPrototypeOf() und Object.setPrototypeOf() sowie sichere Wörterbuch-Objekte ohne Prototyp.
Jedes JavaScript-object ist mit einem anderen object verknüpft, das als sein Prototyp bezeichnet wird, und Property-Suchen wandern diese Kette hinauf. Die alte Methode, diesen Link zu lesen oder zu ändern, war die spezielle __proto__-Property, die jedoch echte Probleme hat: Sie ist ein geerbter Accessor (kein normaler Datenspeicher), verhält sich inkonsistent als object-Schlüssel und kann ausgenutzt werden, um Objekte zu beschädigen. Modernes JavaScript ersetzt sie durch explizite, vorhersehbare Methoden am Object-Konstruktor.
Dieses Kapitel zeigt die standardisierten Prototyp-Methoden — Object.create(), Object.getPrototypeOf() und Object.setPrototypeOf() — sowie die Inspektions-Hilfsfunktionen Object.keys(), Object.values(), Object.entries() und Object.hasOwn(). Anschließend wird der nützlichste Anwendungsfall für ein object ohne Prototyp behandelt: ein sicheres „Wörterbuch" für beliebige Schlüssel.
Einen umfassenden Überblick darüber, wie die Kette funktioniert, finden Sie unter Prototypale Vererbung und mehr.
Einen Prototyp lesen und setzen
Verwenden Sie Object.getPrototypeOf(), um den Prototyp eines object zu lesen, und Object.setPrototypeOf(), um ihn zu ändern. Dies sind die standardisierten Ersatzmethoden für das Lesen und Schreiben von __proto__.
Object.create(proto) erstellt ein brandneues object, dessen Prototyp genau proto ist. Es ist der sauberste Weg, ein object mit einem gewählten Prototyp zu erstellen, ohne __proto__ überhaupt zu verwenden.
Warum __proto__ vermeiden?
__proto__ ist ein Getter/Setter, der auf Object.prototype definiert ist, keine Property, die auf Ihrem object lebt. Dieser Unterschied verursacht zwei praktische Probleme:
- Es kann als Datenschlüssel fehlschlagen oder sich seltsam verhalten. Da
obj.__proto__ = valueden Setter auslöst, kann man unter einem normalen object keinen Schlüssel namens"__proto__"zuverlässig speichern — das Zuweisen eines Nicht-object wird stillschweigend ignoriert, und das Zuweisen eines object ändert den Prototyp, anstatt einen Schlüssel hinzuzufügen. - Es ist eine bekannte Angriffsfläche („Prototype Pollution"). Code, der nicht vertrauenswürdige Schlüssel auf ein object kopiert, kann dazu verleitet werden, in
__proto__zu schreiben, wodurchObject.prototypefür das gesamte Programm verunreinigt wird.
Die standardisierten Methoden sind in ihrer Absicht eindeutig: getPrototypeOf/setPrototypeOf ändern den Prototyp, während normale Property-Schreibvorgänge ihn niemals berühren.
Objekte ohne Prototyp
Object.create(null) erstellt ein object, dessen Prototyp null ist. Es erbt nichts — nicht einmal toString, hasOwnProperty oder den __proto__-Accessor.
Das Wörterbuch-Problem, das es löst
Ein verbreitetes Muster ist die Verwendung eines einfachen object als Zuordnung von string-Schlüsseln zu Werten. Das Problem ist, dass ein einfaches {} bereits Schlüssel von Object.prototype erbt, sodass vom Benutzer gelieferte Schlüssel wie "constructor", "toString" oder "__proto__" mit den geerbten Namen kollidieren und Abfragen zerstören.
Ein null-Prototyp-object hat keine geerbten Schlüssel, sodass sich jeder Schlüssel genau so verhält, wie er geschrieben wurde — einschließlich "__proto__":
Deshalb eignen sich null-Prototyp-Objekte als sichere Wörterbücher für nicht vertrauenswürdige Schlüssel. (Die eingebaute Map ist eine weitere gute Wahl und erlaubt auch Nicht-string-Schlüssel.)
Methoden zu einem null-Prototyp-object hinzufügen
Da kein Prototyp zur Verfügung steht, von dem geerbt werden könnte, weisen Sie Methoden direkt als eigene Properties zu.
Schlüssel, Werte und Einträge iterieren
Object.keys(), Object.values() und Object.entries() geben arrays der eigenen enumerable Properties eines object zurück. Entscheidend ist, dass sie die Prototyp-Kette ignorieren und daher auf normalen und null-Prototyp-Objekten identisch funktionieren. Weitere Informationen finden Sie unter Object.keys, values, entries.
Ein Vorbehalt bei null-Prototyp-Objekten: Sie haben kein geerbtes toString, sodass das direkte Übergeben an Template Literals oder String() einen Fehler wirft. Verwenden Sie stattdessen die Object.*-Hilfsfunktionen (oben) anstatt auf automatische string-Konvertierung zu vertrauen.
Eigene Properties mit Object.hasOwn() prüfen
Um zu testen, ob ein Schlüssel eine eigene Property des object ist (nicht geerbt), bevorzugen Sie Object.hasOwn(). Es ist der moderne Ersatz für obj.hasOwnProperty() und funktioniert auch bei null-Prototyp-Objekten, die überhaupt keine hasOwnProperty-Methode besitzen.
Komposition mit Object.assign()
Wenn ein object das Verhalten mehrerer anderer übernehmen soll, ist Komposition oft klarer als eine einzelne Vererbungskette: Ein object kann nur einen Prototyp haben, aber Object.assign(target, ...sources) kann Methoden aus vielen Quellen zusammenführen, indem es deren eigene enumerable Properties auf das Ziel kopiert.
Object.assign() kopiert auch auf ein null-Prototyp-object und ergibt so ein zusammengesetztes Wörterbuch ohne geerbte Oberfläche:
Vorbehalt bei flachem Kopieren: Object.assign() kopiert Property-Werte, keine tiefen Klone. object- und array-Werte werden als Referenz kopiert, sodass Quelle und Ziel dieselben verschachtelten Objekte teilen.
Für eine tiefe, unabhängige Kopie verwenden Sie stattdessen structuredClone(source).
Best Practices
- Verwenden Sie die standardisierten Methoden, nicht
__proto__. Greifen Sie aufObject.create(),Object.getPrototypeOf()undObject.setPrototypeOf()zurück. Behandeln Sie__proto__als veralteten Accessor, den Sie in eigenem Code vermeiden sollten. - Vermeiden Sie das Ändern von Prototypen nach der Erstellung. Einen gewählten Prototyp zum Erstellungszeitpunkt mit
Object.create(proto)festzulegen ist sauberer und leichter nachzuvollziehen als ihn später mitObject.setPrototypeOf()zu mutieren. - Verwenden Sie
Object.create(null)(oderMap) für Wörterbücher mit nicht vertrauenswürdigen Schlüsseln. Dadurch werden geerbte Schlüssel entfernt und Prototype-Pollution-Überraschungen verhindert. - Bevorzugen Sie
Object.hasOwn()gegenüberobj.hasOwnProperty(). Es ist kürzer, sicherer aufzurufen und funktioniert bei null-Prototyp-Objekten. - Komponieren Sie mit
Object.assign(), wenn Sie Verhalten aus mehreren Quellen benötigen — denken Sie nur daran, dass es sich um eine flache Kopie handelt.
Verwandtes Material finden Sie unter Objekt-Methoden und „this" und Property-Flags und -Deskriptoren.
Fazit
Die modernen Object.*-Prototyp-Methoden geben Ihnen explizite, vorhersehbare Kontrolle über die Prototyp-Kette und ersetzen den eigenwilligen __proto__-Accessor. Object.create(null) erzeugt ein sauberes, prototyp-freies object, das ideal für Wörterbücher ist, während Object.keys/values/entries, Object.hasOwn() und Object.assign() es Ihnen ermöglichen, Objekte unabhängig von ihrem Prototyp sicher zu inspizieren und zu komponieren.