Gibt es eine JavaScript Äquivalent von Java‘S class.getName()
?
Holen Sie sich den Namen eines Objekttyps
Ewen Cartwright
Gibt es ein JavaScript-Äquivalent zu Java?
class.getName()
?
Nein.
ES2015-Update: der Name von class Foo {}
ist Foo.name
. Der Name von thing
‘s-Klasse, unabhängig davon thing
‘s Typ, ist thing.constructor.name
. Eingebaute Konstrukteure in einer ES2015-Umgebung haben die richtige name
Eigentum; zum Beispiel (2).constructor.name
ist "Number"
.
Aber hier sind verschiedene Hacks, die alle auf die eine oder andere Weise herunterfallen:
Hier ist ein Hack, der das tut, was Sie brauchen – seien Sie sich bewusst, dass er den Prototyp des Objekts modifiziert, etwas, das die Leute missbilligen (normalerweise aus gutem Grund).
Object.prototype.getName = function() {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
};
Jetzt haben alle Ihre Objekte die Funktion, getName()
, die den Namen des Konstruktors als Zeichenfolge zurückgibt. Ich habe das in getestet FF3
und IE7
ich kann nicht für andere Implementierungen sprechen.
Wenn Sie das nicht möchten, finden Sie hier eine Diskussion über die verschiedenen Möglichkeiten, Typen in JavaScript zu bestimmen …
Ich habe dies kürzlich aktualisiert, um es etwas ausführlicher zu machen, obwohl es das kaum ist. Korrekturen erwünscht…
Verwendung der constructor
Eigentum…
Jeden object
hat einen Wert für seine constructor
Eigentum, aber je nachdem, wie das object
konstruiert wurde und was Sie mit diesem Wert machen wollen, kann es nützlich sein oder auch nicht.
Im Allgemeinen können Sie die verwenden constructor
Eigenschaft, um den Typ des Objekts wie folgt zu testen:
var myArray = [1,2,3];
(myArray.constructor == Array); // true
Das funktioniert also gut genug für die meisten Bedürfnisse. Das gesagt…
Vorbehalte
Wird nicht funktionieren ÜBERHAUPT in vielen Fällen
Dieses Muster, obwohl gebrochen, ist ziemlich verbreitet:
function Thingy() {
}
Thingy.prototype = {
method1: function() {
},
method2: function() {
}
};
Objects
konstruiert über new Thingy
wird ein haben constructor
Eigenschaft, die darauf hinweist Object
nicht Thingy
. Wir fallen also gleich zu Beginn; man kann einfach nicht vertrauen constructor
in einer Codebasis, die Sie nicht kontrollieren.
Mehrfachvererbung
Ein Beispiel, bei dem es nicht so offensichtlich ist, ist die Verwendung von Mehrfachvererbung:
function a() { this.foo = 1;}
function b() { this.bar = 2; }
b.prototype = new a(); // b inherits from a
Die Dinge funktionieren jetzt nicht so, wie Sie es vielleicht erwarten:
var f = new b(); // instantiate a new object with the b constructor
(f.constructor == b); // false
(f.constructor == a); // true
Sie könnten also unerwartete Ergebnisse erhalten, wenn die object
Ihre Prüfung hat eine andere object
als sein setzen prototype
. Es gibt Möglichkeiten, dies außerhalb des Rahmens dieser Diskussion zu umgehen.
Es gibt andere Verwendungen für die constructor
Eigentum, einige davon interessant, andere nicht so sehr; Im Moment werden wir uns nicht mit diesen Verwendungen befassen, da sie für diese Diskussion nicht relevant sind.
Funktioniert nicht rahmen- und fensterübergreifend
Verwenden .constructor
für die Typprüfung wird unterbrochen, wenn Sie den Typ von Objekten überprüfen möchten, die von verschiedenen kommen window
Objekte, sagen wir die eines Iframes oder eines Popup-Fensters. Dies liegt daran, dass es von jedem Kerntyp eine andere Version gibt constructor
in jedem “Fenster”, dh
iframe.contentWindow.Array === Array // false
Verwendung der instanceof
Operator…
Die instanceof
Operator ist eine saubere Art des Testens object
Art auch, hat aber seine eigenen potenziellen Probleme, genau wie die constructor
Eigentum.
var myArray = [1,2,3];
(myArray instanceof Array); // true
(myArray instanceof Object); // true
Aber instanceof
funktioniert nicht für Literalwerte (weil Literale dies nicht sind Objects
)
3 instanceof Number // false
'abc' instanceof String // false
true instanceof Boolean // false
Die Literale müssen in ein eingeschlossen werden Object
damit instanceof
zum Beispiel arbeiten
new Number(3) instanceof Number // true
Die .constructor
check funktioniert gut für Literale, weil die .
Der Methodenaufruf hüllt die Literale implizit in ihren jeweiligen Objekttyp ein
3..constructor === Number // true
'abc'.constructor === String // true
true.constructor === Boolean // true
Warum zwei Punkte für die 3? Weil Javascript den ersten Punkt als Dezimalpunkt interpretiert 😉
Funktioniert nicht rahmen- und fensterübergreifend
instanceof
funktioniert auch nicht über verschiedene Fenster hinweg, aus dem gleichen Grund wie die constructor
Eigentumsprüfung.
Verwendung der name
Eigentum der constructor
Eigentum…
Funktioniert nicht ÜBERHAUPT in vielen Fällen
Auch hier siehe oben; es ist ziemlich üblich für constructor
völlig falsch und nutzlos zu sein.
Funktioniert NICHT in
Verwenden myObjectInstance.constructor.name
gibt Ihnen eine Zeichenfolge mit dem Namen der constructor
Funktion verwendet, unterliegt jedoch den Einschränkungen bezüglich der constructor
Eigentum, das zuvor erwähnt wurde.
Für IE9 und höher ist dies möglich Monkey-Patch zur Unterstützung:
if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
Object.defineProperty(Function.prototype, 'name', {
get: function() {
var funcNameRegex = /function\s+([^\s(]+)\s*\(/;
var results = (funcNameRegex).exec((this).toString());
return (results && results.length > 1) ? results[1] : "";
},
set: function(value) {}
});
}
Aktualisierte Version aus dem betreffenden Artikel. Dies wurde 3 Monate nach Veröffentlichung des Artikels hinzugefügt, dies ist die vom Autor des Artikels, Matthew Scharley, empfohlene Version. Diese Änderung wurde inspiriert von Kommentare, die auf mögliche Fallstricke hinweisen im vorherigen Code.
if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
Object.defineProperty(Function.prototype, 'name', {
get: function() {
var funcNameRegex = /function\s([^(]{1,})\(/;
var results = (funcNameRegex).exec((this).toString());
return (results && results.length > 1) ? results[1].trim() : "";
},
set: function(value) {}
});
}
Verwenden von Object.prototype.toString
Es stellt sich heraus, wie Details zu diesem Beitragkönnen Sie verwenden Object.prototype.toString
– die Low-Level- und generische Implementierung von toString
– um den Typ für alle eingebauten Typen zu erhalten
Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(/abc/) // [object RegExp]
Object.prototype.toString.call([1,2,3]) // [object Array]
Man könnte eine kurze Hilfsfunktion wie schreiben
function type(obj){
return Object.prototype.toString.call(obj).slice(8, -1);
}
um die Cruft zu entfernen und nur den Typennamen zu erhalten
type('abc') // String
Es wird jedoch zurückkehren Object
für alle benutzerdefinierten Typen.
Vorbehalte für alle…
All dies unterliegt einem potenziellen Problem, und das ist die Frage, wie das betreffende Objekt konstruiert wurde. Hier sind verschiedene Möglichkeiten zum Erstellen von Objekten und die Werte, die die verschiedenen Methoden der Typprüfung zurückgeben:
// using a named function:
function Foo() { this.a = 1; }
var obj = new Foo();
(obj instanceof Object); // true
(obj instanceof Foo); // true
(obj.constructor == Foo); // true
(obj.constructor.name == "Foo"); // true
// let's add some prototypical inheritance
function Bar() { this.b = 2; }
Foo.prototype = new Bar();
obj = new Foo();
(obj instanceof Object); // true
(obj instanceof Foo); // true
(obj.constructor == Foo); // false
(obj.constructor.name == "Foo"); // false
// using an anonymous function:
obj = new (function() { this.a = 1; })();
(obj instanceof Object); // true
(obj.constructor == obj.constructor); // true
(obj.constructor.name == ""); // true
// using an anonymous function assigned to a variable
var Foo = function() { this.a = 1; };
obj = new Foo();
(obj instanceof Object); // true
(obj instanceof Foo); // true
(obj.constructor == Foo); // true
(obj.constructor.name == ""); // true
// using object literal syntax
obj = { foo : 1 };
(obj instanceof Object); // true
(obj.constructor == Object); // true
(obj.constructor.name == "Object"); // true
Obwohl nicht alle Permutationen in dieser Reihe von Beispielen vorhanden sind, gibt es hoffentlich genug, um Ihnen eine Vorstellung davon zu geben, wie chaotisch die Dinge je nach Ihren Bedürfnissen werden können. Nehmen Sie nichts an, wenn Sie nicht genau verstehen, wonach Sie suchen, kann es passieren, dass der Code dort bricht, wo Sie es nicht erwarten, weil Sie die Feinheiten nicht genau kennen.
HINWEIS:
Diskussion über die typeof
Der Operator mag wie eine eklatante Auslassung erscheinen, aber er ist wirklich nicht hilfreich, um festzustellen, ob an object
ist ein gegebener Typ, da er sehr einfach ist. Verstehen wo typeof
ist nützlich ist wichtig, aber ich habe derzeit nicht das Gefühl, dass es für diese Diskussion besonders relevant ist. Mein Geist ist jedoch offen für Veränderungen. 🙂
Verwenden myObjectInstance.constructor.name
gibt Ihnen eine Zeichenfolge mit dem Namen der constructor
Funktion verwendet, unterliegt jedoch den Einschränkungen bezüglich der constructor
Eigentum, das zuvor erwähnt wurde.
Für IE9 und höher ist dies möglich Monkey-Patch zur Unterstützung:
if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
Object.defineProperty(Function.prototype, 'name', {
get: function() {
var funcNameRegex = /function\s+([^\s(]+)\s*\(/;
var results = (funcNameRegex).exec((this).toString());
return (results && results.length > 1) ? results[1] : "";
},
set: function(value) {}
});
}
Aktualisierte Version aus dem betreffenden Artikel. Dies wurde 3 Monate nach Veröffentlichung des Artikels hinzugefügt, dies ist die vom Autor des Artikels, Matthew Scharley, empfohlene Version. Diese Änderung wurde inspiriert von Kommentare, die auf mögliche Fallstricke hinweisen im vorherigen Code.
if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
Object.defineProperty(Function.prototype, 'name', {
get: function() {
var funcNameRegex = /function\s([^(]{1,})\(/;
var results = (funcNameRegex).exec((this).toString());
return (results && results.length > 1) ? results[1].trim() : "";
},
set: function(value) {}
});
}
Verwenden von Object.prototype.toString
Es stellt sich heraus, wie Details zu diesem Beitragkönnen Sie verwenden Object.prototype.toString
– die Low-Level- und generische Implementierung von toString
– um den Typ für alle eingebauten Typen zu erhalten
Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(/abc/) // [object RegExp]
Object.prototype.toString.call([1,2,3]) // [object Array]
Man könnte eine kurze Hilfsfunktion wie schreiben
function type(obj){
return Object.prototype.toString.call(obj).slice(8, -1);
}
um die Cruft zu entfernen und nur den Typennamen zu erhalten
type('abc') // String
Es wird jedoch zurückkehren Object
für alle benutzerdefinierten Typen.
Vorbehalte für alle…
All dies unterliegt einem potenziellen Problem, und das ist die Frage, wie das betreffende Objekt konstruiert wurde. Hier sind verschiedene Möglichkeiten zum Erstellen von Objekten und die Werte, die die verschiedenen Methoden der Typprüfung zurückgeben:
// using a named function:
function Foo() { this.a = 1; }
var obj = new Foo();
(obj instanceof Object); // true
(obj instanceof Foo); // true
(obj.constructor == Foo); // true
(obj.constructor.name == "Foo"); // true
// let's add some prototypical inheritance
function Bar() { this.b = 2; }
Foo.prototype = new Bar();
obj = new Foo();
(obj instanceof Object); // true
(obj instanceof Foo); // true
(obj.constructor == Foo); // false
(obj.constructor.name == "Foo"); // false
// using an anonymous function:
obj = new (function() { this.a = 1; })();
(obj instanceof Object); // true
(obj.constructor == obj.constructor); // true
(obj.constructor.name == ""); // true
// using an anonymous function assigned to a variable
var Foo = function() { this.a = 1; };
obj = new Foo();
(obj instanceof Object); // true
(obj instanceof Foo); // true
(obj.constructor == Foo); // true
(obj.constructor.name == ""); // true
// using object literal syntax
obj = { foo : 1 };
(obj instanceof Object); // true
(obj.constructor == Object); // true
(obj.constructor.name == "Object"); // true
Obwohl nicht alle Permutationen in dieser Reihe von Beispielen vorhanden sind, gibt es hoffentlich genug, um Ihnen eine Vorstellung davon zu geben, wie chaotisch die Dinge je nach Ihren Bedürfnissen werden können. Nehmen Sie nichts an, wenn Sie nicht genau verstehen, wonach Sie suchen, kann es passieren, dass der Code dort bricht, wo Sie es nicht erwarten, weil Sie die Feinheiten nicht genau kennen.
HINWEIS:
Diskussion über die typeof
Der Operator mag wie eine eklatante Auslassung erscheinen, aber er ist wirklich nicht hilfreich, um festzustellen, ob an object
ist ein gegebener Typ, da er sehr einfach ist. Verstehen wo typeof
ist nützlich ist wichtig, aber ich habe derzeit nicht das Gefühl, dass es für diese Diskussion besonders relevant ist. Mein Geist ist jedoch offen für Veränderungen. 🙂
-
Nun, ich dachte mir, ich könnte es auch – der Sinn von Stack Overflow ist es, ein bisschen wie ein Wiki zu sein, und das entspricht viel mehr dieser Absicht, denke ich. Unabhängig davon wollte ich nur etwas gründlich sein.
– Jason Bunting
5. Dezember 2008 um 20:47 Uhr
-
Es ist wichtig zu beachten, dass alle Techniken, die das Objekt untersuchen
constructor
Methode (entweder mit.toString()
oder.name
) funktioniert nicht, wenn Ihr Javascript mit einem Tool wie uglify oder der Rails-Asset-Pipeline minimiert wurde. Die Minimierung benennt den Konstruktor um, sodass Sie am Ende falsche Klassennamen wien
. Wenn Sie sich in diesem Szenario befinden, möchten Sie vielleicht einfach manuell definiere aclassName
-Eigenschaft auf Ihren Objekten und verwenden Sie diese stattdessen.– Gabe Martin-Dempesy
28. Dezember 2012 um 16:12 Uhr
Ewen Cartwright
Die Antwort von Jason Bunting gab mir genug Anhaltspunkte, um zu finden, was ich brauchte:
<<Object instance>>.constructor.name
So zum Beispiel in folgendem Codeabschnitt:
function MyObject() {}
var myInstance = new MyObject();
myInstance.constructor.name
würden zurückkehren "MyObject"
.
-
Der Vollständigkeit halber sollte erwähnt werden, dass die Verwendung von constructor.name nur funktioniert, wenn Sie eine benannte Funktion als Konstruktor verwenden, im Gegensatz zu einer anonymen Funktion, die einer Variablen zugewiesen ist.
– Matthew Crumley
1. Dezember 2008 um 22:24 Uhr
-
Der Vollständigkeit halber sollte erwähnt werden, dass es in IE-Browsern nicht funktioniert – sie unterstützen das „name“-Attribut bei Funktionen nicht.
– Eugen Lazutkin
2. Dezember 2008 um 3:30 Uhr
Daniel Szab
Ein kleiner Trick, den ich verwende:
function Square(){
this.className = "Square";
this.corners = 4;
}
var MySquare = new Square();
console.log(MySquare.className); // "Square"
-
Das gefällt mir nicht besonders. Es ist eher eine Art schmutziger Trick. Wenn Sie andererseits nicht zu viele Konstruktoren haben, könnte es gut funktionieren.
– pimvdb
17. Juni 2011 um 7:56 Uhr
-
@pimvdb: Ich denke, es ist sauberer, als den Prototyp des Objekts zu ändern, a la die akzeptierte Antwort.
– Daniel Szabo
13. Mai 2013 um 0:10 Uhr
-
@DanielSzabo Wenn eine Eigenschaft zwischen allen Instanzen eines Prototyps denselben Wert haben soll, ziehe ich es definitiv vor, sie einfach auf den Prototyp zu setzen – sie auf jede Instanz zu setzen, ist superredundant und die Metadaten fehlen im Prototyp selbst. Allerdings wurde in ES6 die klügste Lösung gewählt: Wenn ja
class Square
Der Name istSquare.name
/MySquare.constructor.name
eher, alsSquare.prototype.name
; durch setzenname
Bei der Konstruktorfunktion verunreinigt es weder den Prototyp noch eine Instanz, ist aber von beiden aus zugänglich.– Andy
13. November 2017 um 23:21 Uhr
Saul
Aktualisieren
Um genau zu sein, ich denke, OP hat nach einer Funktion gefragt, die den Konstruktornamen für ein bestimmtes Objekt abruft. In Bezug auf Javascript, object
hat keinen Typ, ist aber ein Typ von und an sich. Unterschiedliche Objekte können jedoch unterschiedlich sein Konstrukteure.
Object.prototype.getConstructorName = function () {
var str = (this.prototype ? this.prototype.constructor : this.constructor).toString();
var cname = str.match(/function\s(\w*)/)[1];
var aliases = ["", "anonymous", "Anonymous"];
return aliases.indexOf(cname) > -1 ? "Function" : cname;
}
new Array().getConstructorName(); // returns "Array"
(function () {})().getConstructorName(); // returns "Function"
Notiz: Das folgende Beispiel ist veraltet.
EIN Blogeintrag verlinkt von Christian Sciberras enthält ein gutes Beispiel dafür, wie es geht. Nämlich durch Erweiterung des Object-Prototyps:
if (!Object.prototype.getClassName) {
Object.prototype.getClassName = function () {
return Object.prototype.toString.call(this).match(/^\[object\s(.*)\]$/)[1];
}
}
var test = [1,2,3,4,5];
alert(test.getClassName()); // returns Array
Verwenden von Object.prototype.toString
Es stellt sich heraus, dass Sie, wie in diesem Beitrag beschrieben, Object.prototype.toString – die Low-Level- und generische Implementierung von toString – verwenden können, um den Typ für alle integrierten Typen zu erhalten
Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(/abc/) // [object RegExp]
Object.prototype.toString.call([1,2,3]) // [object Array]
Man könnte eine kurze Hilfsfunktion wie schreiben
function type(obj){
return Object.prototype.toString.call(obj]).match(/\s\w+/)[0].trim()
}
return [object String] as String
return [object Number] as Number
return [object Object] as Object
return [object Undefined] as Undefined
return [object Function] as Function
-
Sie müssen Regex nicht verwenden, um den Objektnamen zu analysieren. Benutz einfach
.slice()
:Object.prototype.toString.call(obj).slice( 8, -1 );
– bobobobo
3. Januar 2018 um 18:39 Uhr
Eli
Hier ist eine Lösung, die ich mir ausgedacht habe, die die Mängel von instanceof behebt. Es kann die Typen eines Objekts von Cross-Windows und Cross-Frames überprüfen und hat keine Probleme mit primitiven Typen.
function getType(o) {
return Object.prototype.toString.call(o).match(/^\[object\s(.*)\]$/)[1];
}
function isInstance(obj, type) {
var ret = false,
isTypeAString = getType(type) == "String",
functionConstructor, i, l, typeArray, context;
if (!isTypeAString && getType(type) != "Function") {
throw new TypeError("type argument must be a string or function");
}
if (obj !== undefined && obj !== null && obj.constructor) {
//get the Function constructor
functionConstructor = obj.constructor;
while (functionConstructor != functionConstructor.constructor) {
functionConstructor = functionConstructor.constructor;
}
//get the object's window
context = functionConstructor == Function ? self : functionConstructor("return window")();
//get the constructor for the type
if (isTypeAString) {
//type is a string so we'll build the context (window.Array or window.some.Type)
for (typeArray = type.split("."), i = 0, l = typeArray.length; i < l && context; i++) {
context = context[typeArray[i]];
}
} else {
//type is a function so execute the function passing in the object's window
//the return should be a constructor
context = type(context);
}
//check if the object is an instance of the constructor
if (context) {
ret = obj instanceof context;
if (!ret && (type == "Number" || type == "String" || type == "Boolean")) {
ret = obj.constructor == context
}
}
}
return ret;
}
isInstance erfordert zwei Parameter: ein Objekt und einen Typ. Der eigentliche Trick bei der Funktionsweise besteht darin, dass überprüft wird, ob das Objekt aus demselben Fenster stammt, und wenn nicht, das Fenster des Objekts abgerufen wird.
Beispiele:
isInstance([], "Array"); //true
isInstance("some string", "String"); //true
isInstance(new Object(), "Object"); //true
function Animal() {}
function Dog() {}
Dog.prototype = new Animal();
isInstance(new Dog(), "Dog"); //true
isInstance(new Dog(), "Animal"); //true
isInstance(new Dog(), "Object"); //true
isInstance(new Animal(), "Dog"); //false
Das Typargument kann auch eine Callback-Funktion sein, die einen Konstruktor zurückgibt. Die Rückruffunktion erhält einen Parameter, der das Fenster des bereitgestellten Objekts ist.
Beispiele:
//"Arguments" type check
var args = (function() {
return arguments;
}());
isInstance(args, function(w) {
return w.Function("return arguments.constructor")();
}); //true
//"NodeList" type check
var nl = document.getElementsByTagName("*");
isInstance(nl, function(w) {
return w.document.getElementsByTagName("bs").constructor;
}); //true
Beachten Sie, dass IE < 9 den Konstruktor nicht für alle Objekte bereitstellt, sodass der obige Test für NodeList falsch zurückgeben würde, und auch ein isInstance(alert, "Function") würde falsch zurückgeben.
-
Sie müssen Regex nicht verwenden, um den Objektnamen zu analysieren. Benutz einfach
.slice()
:Object.prototype.toString.call(obj).slice( 8, -1 );
– bobobobo
3. Januar 2018 um 18:39 Uhr
Ich habe eigentlich nach etwas ähnlichem gesucht und bin auf diese Frage gestoßen. So bekomme ich Typen: jsfiddle
var TypeOf = function ( thing ) {
var typeOfThing = typeof thing;
if ( 'object' === typeOfThing ) {
typeOfThing = Object.prototype.toString.call( thing );
if ( '[object Object]' === typeOfThing ) {
if ( thing.constructor.name ) {
return thing.constructor.name;
}
else if ( '[' === thing.constructor.toString().charAt(0) ) {
typeOfThing = typeOfThing.substring( 8,typeOfThing.length - 1 );
}
else {
typeOfThing = thing.constructor.toString().match( /function\s*(\w+)/ );
if ( typeOfThing ) {
return typeOfThing[1];
}
else {
return 'Function';
}
}
}
else {
typeOfThing = typeOfThing.substring( 8,typeOfThing.length - 1 );
}
}
return typeOfThing.charAt(0).toUpperCase() + typeOfThing.slice(1);
}
@ user34660 Ich denke, wir können sicher davon ausgehen, dass es den Namen eines Objekttyps erhält.
– Stapelunterlauf
24. Dezember 2018 um 16:54 Uhr
@StackUnderflow: Außer eigentlich nicht. Es erhält den Namen eines Objekts Klassewelches ist nicht das gleiche wie ein Objekt Art.
– Jörg W Mittag
9. September 2019 um 14:35 Uhr
@JörgWMittag Ah ja, natürlich. Siehst du, was passiert, wenn du sicher herumgehst und Dinge annimmst?
– Stapelunterlauf
9. September 2019 um 18:37 Uhr