Warum könnte der Klassenname zur Laufzeit nützlich sein?
\n\nStellen Sie sich vor: Sie schreiben ein komplexes System in TypeScript. Sie haben eine Klassenhierarchie — User, Admin, Moderator. Irgendwo tief im Code erhalten Sie ein Objekt, wissen aber nicht, was es wirklich ist. In der Konsole sehen Sie nur object.
Oder Sie bauen einen Logger, der nicht nur [Object], sondern einen aussagekräftigen Klassennamen ausgeben soll: [User: John]. Oder Sie schreiben einen Decorator, der je nach Typ des Objekts entscheiden soll, wie es verarbeitet wird.
Genau hier kommt die Frage mit tausend Aufrufen auf Stack Overflow ins Spiel: "Get an object's class name at runtime". Heute geben wir nicht nur eine Antwort, sondern schauen unter die Haube von TypeScript und JavaScript, um zu verstehen, wie das wirklich funktioniert.
\n\nJa, TypeScript ist ein Overlay über JavaScript. Die ganze Magie der Typen verschwindet nach der Kompilierung. Aber der Klassenname ist kein Typ, sondern eine Eigenschaft des Prototyps. Und er bleibt uns zur Laufzeit erhalten.
\n\nDer einfachste Weg: constructor.name
\n\nWenn Sie jemals nach dieser Frage gegoogelt haben, haben Sie sicher diesen Einzeiler gesehen:
\n\nclass User {\n constructor(public name: string) {}\n}\n\nconst user = new User("Alice");\nconsole.log(user.constructor.name); // "User"\n\nFunktioniert es? Ja. Schnell? Ja. Aber es gibt Nuancen, die Sie beißen können, wenn Sie nicht wissen, wie es funktioniert.
\n\nWie funktioniert das unter der Haube?
\n\nJedes Objekt in JavaScript hat eine Eigenschaft constructor. Dies ist ein Verweis auf die Funktion, die das Objekt erstellt hat. Und jede Funktion (eine Klasse ist auch eine Funktion) hat eine Eigenschaft name.
Wenn Sie user.constructor.name schreiben, macht die Engine Folgendes:
- \n
- Sie geht die Prototypenkette des Objekts
userentlang. \n - Sie findet die Eigenschaft
constructoraufUser.prototype. \n - Sie liest
nameder KonstruktorfunktionUser. \n
Dies ist eine günstige Operation, aber nicht kostenlos. In den meisten Fällen werden Sie es nicht einmal bemerken.
\n\nWann funktioniert constructor.name nicht?
\n\nHier ist ein klassischer Fallstrick:
\n\nconst obj = Object.create(null);\nconsole.log(obj.constructor); // undefined\n// console.log(obj.constructor.name); // TypeError!\n\nEin Objekt, das mit Object.create(null) erstellt wurde, hat keinen Prototypen. Es hat kein constructor. Seien Sie vorsichtig.
Der zweite Fall ist die Minifizierung. Wenn Sie einen Bundler (Webpack, Terser) verwenden, der Klassen umbenennt, kann constructor.name etwas wie t oder e zurückgeben. Dies ist nur ein Problem für die Produktion, aber man sollte es wissen.
Alternative: instanceof und benutzerdefinierte Prüfungen
\n\nManchmal brauchen Sie nicht nur den Namen, sondern eine Prüfung: "Ist dieses Objekt eine Instanz der Klasse X?"
\n\nclass Animal {}\nclass Dog extends Animal {}\n\nconst dog = new Dog();\n\nconsole.log(dog instanceof Dog); // true\nconsole.log(dog instanceof Animal); // true\nconsole.log(dog instanceof Object); // true\n\ninstanceof funktioniert über die Prototypenkette. Es prüft, ob Dog.prototype in der Prototypenkette des Objekts dog vorhanden ist. Wenn ja — true.
Aber instanceof liefert Ihnen keinen String mit dem Namen. Es liefert einen Boolean-Wert. Für Logs oder Serialisierung ist das nicht geeignet.
Kombinieren: Prüfung + Name
\n\nSie können ein Utility schreiben, das zuerst den Typ prüft und dann den Namen zurückgibt:
\n\nfunction getClassName(obj: any): string | null {\n if (obj === null || obj === undefined) return null;\n if (typeof obj !== 'object' && typeof obj !== 'function') return null;\n\n // Schutz vor Object.create(null)\n if (!obj.constructor) return 'Object (no prototype)';\n\n const name