Pregunta ¿Cómo obtengo el nombre del tipo de un objeto en JavaScript?


¿Hay un JavaScript equivalente a Java? class.getName()?


1051
2017-12-01 22:06


origen


Respuestas:


¿Hay un JavaScript equivalente a Java? class.getName()?

No.

Actualización ES2015: el nombre de class Foo {} es Foo.name. El nombre de thingde clase, independientemente de thingtipo de, es thing.constructor.name. Los constructores Builtin en un entorno ES2015 tienen la correcta name propiedad; por ejemplo (2).constructor.name es "Number".


Pero aquí hay varios hacks que caen de una forma u otra:

Aquí hay un truco que hará lo que necesita: tenga en cuenta que modifica el prototipo del Objeto, algo que la gente desaprueba (generalmente por una buena razón)

Object.prototype.getName = function() { 
   var funcNameRegex = /function (.{1,})\(/;
   var results = (funcNameRegex).exec((this).constructor.toString());
   return (results && results.length > 1) ? results[1] : "";
};

Ahora, todos tus objetos tendrán la función, getName(), que devolverá el nombre del constructor como una cadena. He probado esto en FF3 y IE7No puedo hablar de otras implementaciones.

Si no quiere hacer eso, aquí hay una discusión sobre las diversas formas de determinar los tipos en JavaScript ...


Recientemente lo actualicé para ser un poco más exhaustivo, aunque no es eso. Correcciones bienvenidas ...

Utilizando el constructor propiedad...

Cada object tiene un valor para su constructor propiedad, pero dependiendo de cómo object fue construido, así como lo que quiere hacer con ese valor, puede o no ser útil.

En términos generales, puede usar constructor propiedad para probar el tipo del objeto como tal:

var myArray = [1,2,3];
(myArray.constructor == Array); // true

Entonces, eso funciona lo suficientemente bien para la mayoría de las necesidades. Eso dijo ...

Advertencias

No trabajará EN ABSOLUTO en muchos casos

Este patrón, aunque roto, es bastante común:

function Thingy() {
}
Thingy.prototype = {
    method1: function() {
    },
    method2: function() {
    }
};

Objects construido a través de new Thingy tendrá un constructor propiedad que apunta a Objectno Thingy. Así que caemos desde el principio; simplemente no puedes confiar constructor en una base de código que no controlas

Herencia Múltiple

Un ejemplo donde no es tan obvio es el uso de herencia múltiple:

function a() { this.foo = 1;}
function b() { this.bar = 2; }
b.prototype = new a(); // b inherits from a

Las cosas ahora no funcionan como es de esperar:

var f = new b(); // instantiate a new object with the b constructor
(f.constructor == b); // false
(f.constructor == a); // true

Por lo tanto, puede obtener resultados inesperados si object su prueba tiene una diferente object establecer como su prototype. Hay formas de evitar esto fuera del alcance de esta discusión.

Hay otros usos para el constructor propiedad, algunas interesantes, otras no tanto; por ahora no profundizaremos en esos usos ya que no es relevante para esta discusión.

No funcionará el marco cruzado y la ventana cruzada

Utilizando .constructor para verificar el tipo se romperá cuando desee verificar el tipo de objetos procedentes de diferentes window objetos, por ejemplo, de un iframe o una ventana emergente. Esto se debe a que hay una versión diferente de cada tipo de núcleo constructor en cada 'ventana', es decir

iframe.contentWindow.Array === Array // false

Utilizando el instanceof operador...

los instanceof operador es una manera limpia de probar object escriba también, pero tiene sus propios problemas potenciales, al igual constructor propiedad.

var myArray = [1,2,3];
(myArray instanceof Array); // true
(myArray instanceof Object); // true

Pero instanceof no funciona para valores literales (porque los literales no son Objects)

3 instanceof Number // false
'abc' instanceof String // false
true instanceof Boolean // false

Los literales deben estar envueltos en un Object para poder instanceofpara trabajar, por ejemplo

new Number(3) instanceof Number // true

los .constructor cheque funciona bien para literales porque el . La invocación al método implícitamente envuelve los literales en su respectivo tipo de objeto

3..constructor === Number // true
'abc'.constructor === String // true
true.constructor === Boolean // true

¿Por qué dos puntos para el 3? Porque Javascript interpreta el primer punto como un punto decimal;)

No funcionará el marco cruzado y la ventana cruzada

instanceof tampoco funcionará en diferentes ventanas, por la misma razón que constructor control de propiedad.


Utilizando el name propiedad de la constructor propiedad...

No funciona EN ABSOLUTO en muchos casos

De nuevo, mira arriba; es bastante común constructor estar completa y completamente equivocado e inútil.

NO funciona en <IE9

Utilizando myObjectInstance.constructor.name le dará una cadena que contiene el nombre del constructor función utilizada, pero está sujeta a las advertencias sobre constructor propiedad que se mencionó anteriormente.

Para IE9 y superior, puedes mono-parche en apoyo:

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) {}
    });
}

Versión actualizada del artículo en cuestión. Esto fue agregado 3 meses después de la publicación del artículo, esta es la versión recomendada para usar por el autor del artículo Matthew Scharley. Este cambio fue inspirado por comentarios señalando riesgos potenciales en el código anterior.

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) {}
    });
}

Usando Object.prototype.toString

Resulta que, como esta publicación detalla, puedes usar Object.prototype.toString - el bajo nivel y la implementación genérica de toString - para obtener el tipo de todos los tipos incorporados

Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(/abc/) // [object RegExp]
Object.prototype.toString.call([1,2,3]) // [object Array]

Uno podría escribir una función auxiliar corta tal como

function type(obj){
    return Object.prototype.toString.call(obj).slice(8, -1);
}

para eliminar el cruft y obtener solo el nombre del tipo

type('abc') // String

Sin embargo, volverá Object para todos los tipos definidos por el usuario.


Advertencias para todos ...

Todos estos están sujetos a un problema potencial, y esa es la cuestión de cómo se construyó el objeto en cuestión. Aquí hay varias formas de construir objetos y los valores que devolverán los diferentes métodos de verificación de tipos:

// 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

Si bien no todas las permutaciones están presentes en este conjunto de ejemplos, es de esperar que haya suficiente para proporcionarle una idea acerca de qué tan complicadas pueden ser las cosas dependiendo de sus necesidades. No asumas nada, si no entiendes exactamente lo que estás buscando, puedes terminar rompiendo el código donde no lo esperas debido a la falta de comprensión de las sutilezas.

NOTA:

Discusión de la typeof el operador puede parecer una omisión evidente, pero realmente no es útil para ayudar a identificar si object es un tipo dado, ya que es muy simplista. Entendiendo donde typeof es útil, pero actualmente no creo que sea terriblemente relevante para esta discusión. Sin embargo, mi mente está abierta al cambio. :)


1399
2017-12-01 22:21



La respuesta de Jason Bunting me dio suficiente pista para encontrar lo que necesitaba:

<<Object instance>>.constructor.name

Entonces, por ejemplo, en el siguiente fragmento de código:

function MyObject() {}
var myInstance = new MyObject();

myInstance.constructor.name volvería "MyObject".


103
2018-06-16 22:25



Un pequeño truco que uso:

function Square(){
    this.className = "Square";
    this.corners = 4;
}

var MySquare = new Square();
console.log(MySquare.className); // "Square"

24
2017-08-28 15:59



Actualizar

Para ser precisos, creo que OP solicitó una función que recupera el nombre del constructor para un objeto en particular. En términos de Javascript, object no tiene un tipo pero es un tipo de y en sí mismo. Sin embargo, diferentes objetos pueden tener diferente constructores.

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"


Nota: el siguiente ejemplo está en desuso.

UN entrada en el blog vinculado por Christian Sciberras contiene un buen ejemplo de cómo hacerlo. A saber, al extender el prototipo de objeto:

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

16
2018-03-24 12:26



Usando Object.prototype.toString

Resulta que, como se detalla en esta publicación, puede usar Object.prototype.toString - el nivel bajo y la implementación genérica de toString - para obtener el tipo de todos los tipos incorporados

Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(/abc/) // [object RegExp]
Object.prototype.toString.call([1,2,3]) // [object Array]

Uno podría escribir una función auxiliar corta tal como

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

9
2018-03-05 14:42



Aquí hay una solución que he encontrado que resuelve las deficiencias de instanceof. Puede verificar los tipos de un objeto desde ventanas cruzadas y marcos cruzados y no tiene problemas con los tipos primitivos.

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 requiere dos parámetros: un objeto y un tipo. El verdadero truco de cómo funciona es que comprueba si el objeto es de la misma ventana y si no recibe la ventana del objeto.

Ejemplos:

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

El argumento de tipo también puede ser una función de devolución de llamada que devuelve un constructor. La función de devolución de llamada recibirá un parámetro que es la ventana del objeto proporcionado.

Ejemplos:

//"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

Una cosa a tener en cuenta es que IE <9 no proporciona el constructor en todos los objetos, por lo que la prueba anterior para NodeList devolvería false y también una isInstance (alerta, "Función") devolvería false.


8
2017-08-12 19:38



Utilizar constructor.name cuando puedes, y la función de expresión regular cuando no puedo.

Function.prototype.getName = function(){
  if (typeof this.name != 'undefined')
    return this.name;
  else
    return /function (.+)\(/.exec(this.toString())[1];
};

6
2017-10-23 15:04