Uzyskaj nazwę typu obiektu


1193

Czy istnieje javascript równoważne Javy s” class.getName()?


To pytanie zakłada, że ​​wiemy, co robi klasa.getName () Java. Ponieważ nie jestem, nie jestem pewien, czy odpowiedzi mi pomogą.
user34660,

2
@ user34660 Myślę, że możemy bezpiecznie założyć, że to, co robi, otrzymuje nazwę typu obiektu.
Stack Underflow

1
@StackUnderflow: Tyle że tak naprawdę nie jest. Pobiera nazwę klasy obiektu , która nie jest taka sama jak typ obiektu .
Jörg W Mittag

1
@ JörgWMittag Ach tak, oczywiście. Widzisz, co się stanie, kiedy będziesz bezpiecznie chodzić, zakładając różne rzeczy?
Stack Underflow

Odpowiedzi:


1540

Czy istnieje JavaScript w Javie class.getName()?

Nie .

Aktualizacja ES2015 : nazwa class Foo {}toFoo.name . Nazwa thingklasy, niezależnie od thingtypu, to thing.constructor.name. Wbudowane konstruktory w środowisku ES2015 mają poprawną namewłaściwość; na przykład (2).constructor.namejest "Number".


Ale oto różne hacki, które wszystkie upadają w ten czy inny sposób:

Oto hack, który zrobi to, czego potrzebujesz - pamiętaj, że modyfikuje prototyp obiektu, coś, na co ludzie patrzą z niechęcią (zwykle z dobrego powodu)

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

Teraz wszystkie twoje obiekty będą miały funkcję getName(), która zwróci nazwę konstruktora jako ciąg znaków. Przetestowałem to FF3i IE7nie mogę mówić o innych implementacjach.

Jeśli nie chcesz tego robić, oto dyskusja na temat różnych sposobów określania typów w JavaScript ...


Niedawno zaktualizowałem to, aby było nieco bardziej wyczerpujące, chociaż nie jest to wcale takie trudne. Mile widziane poprawki ...

Korzystanie z constructorwłaściwości ...

Każda objectma wartość dla swojej constructorwłaściwości, ale w zależności od tego, jak objectzostała skonstruowana, a także od tego, co chcesz zrobić z tą wartością, może to być przydatne lub nie.

Ogólnie rzecz biorąc, możesz użyć constructorwłaściwości do przetestowania typu obiektu w następujący sposób:

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

To działa wystarczająco dobrze dla większości potrzeb. To mówi...

Ostrzeżenia

W wielu przypadkach nie będzie działać WSZYSTKO

Ten wzór, choć zepsuty, jest dość powszechny:

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

Objectszbudowany za pośrednictwem new Thingybędzie miał constructorwłaściwość, która wskazuje Object, a nie Thingy. Więc upadamy od samego początku; po prostu nie możesz zaufać constructorbazie kodu, której nie kontrolujesz.

Wielokrotne dziedziczenie

Przykładem, w którym nie jest to tak oczywiste, jest wielokrotne dziedziczenie:

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

Rzeczy nie działają teraz, jak można się spodziewać:

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

Możesz więc otrzymać nieoczekiwane wyniki, jeśli objecttwoje testy mają inny objectzestaw prototype. Istnieją na to sposoby poza zakresem tej dyskusji.

Istnieją inne zastosowania constructornieruchomości, niektóre interesujące, inne nie tak bardzo; na razie nie zagłębimy się w te zastosowania, ponieważ nie ma to znaczenia w tej dyskusji.

Nie działa cross-frame i cross-window

Używanie .constructordo sprawdzania typu zepsuje się, gdy chcesz sprawdzić typ obiektów pochodzących z różnych windowobiektów, np. Iframe lub wyskakującego okna. Wynika to z faktu, że constructorw każdym „oknie” jest inna wersja każdego typu rdzenia , tj

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

Korzystanie z instanceofoperatora ...

instanceofOperator jest czysty sposób testowania objecttypu, jak również, ale ma swoje potencjalne problemy, podobnie jak constructornieruchomości.

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

Ale instanceofnie działa dla wartości literalnych (ponieważ literały nie są Objects)

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

Literały muszą być owinięte np. ObjectW celu instanceofpracy

new Number(3) instanceof Number // true

.constructorCheck działa dobrze dla literałów, ponieważ .wywołania metody niejawnie zawija literały w ich odpowiedniego typu obiektu

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

Dlaczego dwie kropki na 3? Ponieważ Javascript interpretuje pierwszą kropkę jako przecinek dziesiętny;)

Nie działa cross-frame i cross-window

instanceofrównież nie będzie działać w różnych oknach z tego samego powodu, co constructorsprawdzenie właściwości.


Korzystanie z namewłaściwości constructornieruchomości ...

Nie działa w ogóle w wielu przypadkach

Ponownie, patrz wyżej; dość powszechne jest constructorto, że jest całkowicie i całkowicie błędne i bezużyteczne.

NIE działa w <IE9

Użycie myObjectInstance.constructor.nameda ci ciąg zawierający nazwę constructorużytej funkcji, ale podlega zastrzeżeniom dotyczącym constructorwcześniej wspomnianej właściwości.

W IE9 i nowszych wersjach możesz wesprzeć małpą łatkę :

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

Zaktualizowana wersja z tego artykułu. Zostało to dodane 3 miesiące po opublikowaniu artykułu, jest to zalecana wersja do użycia przez autora artykułu Matthew Scharley. Ta zmiana została zainspirowana komentarzami wskazującymi potencjalne pułapki w poprzednim kodzie.

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

Korzystanie z Object.prototype.toString

Okazuje się, że jako szczegóły tego postu możesz użyć Object.prototype.toString- niskiego poziomu i ogólnej implementacji toString- aby uzyskać typ dla wszystkich wbudowanych typów

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

Można napisać krótką funkcję pomocniczą, taką jak

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

aby usunąć cruft i uzyskać tylko nazwę typu

type('abc') // String

Zwróci jednak Objectwszystkie typy zdefiniowane przez użytkownika.


Ostrzeżenia dla wszystkich ...

Wszystkie z nich wiążą się z jednym potencjalnym problemem, a mianowicie z tym, jak skonstruowano przedmiotowy obiekt. Oto różne sposoby budowania obiektów i wartości, które zwrócą różne metody sprawdzania typu:

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

Chociaż nie wszystkie permutacje są obecne w tym zestawie przykładów, mam nadzieję, że wystarczy, aby dać ci wyobrażenie o tym, jak bałagan może się stać w zależności od twoich potrzeb. Nie zakładaj niczego, jeśli nie rozumiesz dokładnie, czego szukasz, możesz skończyć na łamaniu kodu tam, gdzie się tego nie spodziewasz, z powodu braku podejrzeń o subtelności.

UWAGA:

Dyskusja na temat typeofoperatora może wydawać się rażącym pominięciem, ale tak naprawdę nie jest przydatna w pomaganiu w określeniu, czy objectdany typ jest dany, ponieważ jest bardzo uproszczony. Zrozumienie, gdzie typeofjest przydatny, jest ważne, ale obecnie nie uważam, aby miało to ogromne znaczenie w tej dyskusji. Mój umysł jest jednak otwarty na zmiany. :)


58
Pomyślałem, że też mogę - celem przepełnienia stosu jest trochę jak wiki, i myślę, że jest to bardziej zgodne z tym zamiarem. Niezależnie od tego, chciałem być trochę dokładny.
Jason Bunting,

Ponowna iteracja odpowiedzi poniżej --- twoje rozszerzenie do prototypu Object nie działa w IE8 - czy ktoś wie, co by działało w IE8?
Adam,

5
Będzie działać, jeśli zrobisz to tak, jak ta funkcja a () {this.a = 1;} funkcja b () {this.b = 2; } b.prototype = new a (); // b dziedziczy po b.prototype.constructor = b; // Prawidłowy sposób dziedziczenia prototypowego var f = new b (); // utwórz nowy obiekt za pomocą konstruktora b (f.constructor == b); // PRAWDA (f.constructor == a); // FALSE
Boris Hamanov

9
Tak właśnie powinna wyglądać większość odpowiedzi na StackOverflow. (nie bierz długości odpowiedzi jako parametru definiującego, ale kompleksowość)
kumarharsh

44
Ważne jest, aby pamiętać, że wszelkie techniki sprawdzające constructormetodę obiektu (za pomocą .toString()lub .name) nie będą działać, jeśli JavaScript został zminimalizowany za pomocą narzędzia takiego jak uglify lub potoku zasobów Rails. Minification zmienia nazwę konstruktora, więc skończysz z niepoprawnymi nazwami klas, takimi jak n. Jeśli jesteś w tym scenariuszu, możesz po prostu ręcznie zdefiniować classNamewłaściwość na swoich obiektach i użyć jej zamiast tego.
Gabe Martin-Dempesy,

126

Odpowiedź Jasona Buntinga dostarczyła mi wystarczającej wskazówki, by znaleźć to, czego potrzebowałem:

<<Object instance>>.constructor.name

Na przykład w następującym fragmencie kodu:

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

myInstance.constructor.namewróciłby "MyObject".


22
Dla kompletności warto wspomnieć, że użycie konstruktora.nazwa działa tylko wtedy, gdy użyto funkcji nazwanej jako konstruktora, a nie funkcji anonimowej przypisanej do zmiennej.
Matthew Crumley,

20
Dla kompletności warto wspomnieć, że nie działa w przeglądarkach IE --- nie obsługują one atrybutu „name” na funkcjach.
Eugene Lazutkin

2
@Eugene - zapomniałem o tym ... Wydaje mi się, że spędziłem zbyt dużo czasu, robiąc javascript poza przeglądarkami.
Matthew Crumley,

2
function getType(o) { return o && o.constructor && o.constructor.name }
justin.m. pościg

To jest TAK kompleksowe.
ibic

26

Mała sztuczka, której używam:

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

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

11
Nie podoba mi się to szczególnie. To bardziej brudna sztuczka. Z drugiej strony, jeśli nie masz zbyt wielu konstruktorów, może działać dobrze.
pimvdb,

13
@pimvdb: Myślę, że to czystsze niż modyfikacja prototypu obiektu, a la zaakceptowana odpowiedź.
Daniel Szabo

4
@DanielSzabo, jeśli właściwość powinna mieć tę samą wartość między wszystkimi instancjami prototypu, zdecydowanie wolę po prostu umieścić ją na prototypie - umieszczenie jej w każdej instancji jest nadmiarowe, a metadanych brakuje w samym prototypie. To powiedziawszy, najmądrzejsze rozwiązanie zostało przyjęte w ES6: jeśli tak class Square, nazwa to Square.name/ MySquare.constructor.nameraczej niż Square.prototype.name; włączenie namefunkcji konstruktora nie zanieczyszcza prototypu ani żadnej instancji, ale jest dostępne z dowolnego z nich.
Andy,

17

Aktualizacja

Mówiąc ściślej, myślę, że OP poprosił o funkcję, która pobiera nazwę konstruktora dla określonego obiektu. Pod względem Javascript objectnie ma typu, ale jest rodzajem samego w sobie . Jednak różne obiekty mogą mieć różne konstruktory .

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"

 


Uwaga: poniższy przykład jest przestarzały.

Wpis na blogu, do którego prowadzi Christian Sciberras, zawiera dobry przykład tego, jak to zrobić. Mianowicie poprzez rozszerzenie prototypu Object:

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

2
Fajnie, ale znów chcemy nazwać: JS nie ma klas.
mikemaccana

@nailer - zalecam korzystanie z zaktualizowanej funkcji, starsza jest przechowywana tylko z przyczyn historycznych.
Saul

Działa to, ale należy zauważyć, że można to zrobić bez modyfikowania Object.prototype, poprzez utworzenie funkcji, która przyjmuje obiekt jako pierwszy argument i używa go zamiast „this” wewnątrz funkcji.
Matt Browne,

2
@Matt - Jasne. To jest po prostu, że o metodę obiektu jest bardziej lakoniczny: test.getClassName()vs getClassName.apply(test).
Saul

12

Korzystanie z Object.prototype.toString

Okazuje się, że jako szczegóły tego postu możesz użyć Object.prototype.toString - niskiego poziomu i ogólnej implementacji toString - aby uzyskać typ dla wszystkich wbudowanych typów

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

Można napisać krótką funkcję pomocniczą, taką jak

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

6
Nie musisz używać wyrażenia regularnego do analizowania nazwy obiektu. Wystarczy użyć .slice():Object.prototype.toString.call(obj).slice( 8, -1 );
bobobobo

9

Oto rozwiązanie, które wymyśliłem, które rozwiązuje niedociągnięcia instancji. Może sprawdzać typy obiektów z okien krzyżowych i ramek krzyżowych i nie ma problemów z typami pierwotnymi.

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 wymaga dwóch parametrów: obiektu i typu. Prawdziwą sztuczką w jego działaniu jest to, że sprawdza, czy obiekt pochodzi z tego samego okna, a jeśli nie, pobiera okno obiektu.

Przykłady:

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

Argument typu może być także funkcją zwrotną, która zwraca konstruktor. Funkcja zwrotna otrzyma jeden parametr, którym jest okno podanego obiektu.

Przykłady:

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

Należy pamiętać, że IE <9 nie zapewnia konstruktora dla wszystkich obiektów, więc powyższy test dla NodeList zwróci false, a także isInstance (alert, „Funkcja”) zwróci false.


8

Tak naprawdę szukałem podobnej rzeczy i natknąłem się na to pytanie. Oto jak otrzymuję typy: 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);
}

7

Powinieneś używać somevar.constructor.namejak:

    
    const getVariableType = a => a.constructor.name.toLowerCase();

    const d = new Date();
    const res1 = getVariableType(d); // 'date'
    const num = 5;
    const res2 = getVariableType(num); // 'number'
    const fn = () => {};
    const res3 = getVariableType(fn); // 'function'

    console.log(res1); // 'date'
    console.log(res2); // 'number'
    console.log(res3); // 'function'


6

Użyj, constructor.namekiedy możesz, i regex, gdy nie mogę.

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

6

Funkcja kind () z Agave.JS zwróci:

  • najbliższy prototyp w drzewie spadkowym
  • dla zawsze prymitywnych typów, takich jak „null” i „undefined”, nazwa pierwotna.

Działa na wszystkich obiektach i prymitywach JS, niezależnie od tego, jak zostały utworzone , i nie ma żadnych niespodzianek. Przykłady:

Liczby

kind(37) === 'Number'
kind(3.14) === 'Number'
kind(Math.LN2) === 'Number'
kind(Infinity) === 'Number'
kind(Number(1)) === 'Number'
kind(new Number(1)) === 'Number'

NaN

kind(NaN) === 'NaN'

Smyczki

kind('') === 'String'
kind('bla') === 'String'
kind(String("abc")) === 'String'
kind(new String("abc")) === 'String'

Booleany

kind(true) === 'Boolean'
kind(false) === 'Boolean'
kind(new Boolean(true)) === 'Boolean'

Tablice

kind([1, 2, 4]) === 'Array'
kind(new Array(1, 2, 3)) === 'Array'

Obiekty

kind({a:1}) === 'Object'
kind(new Object()) === 'Object'

Daktyle

kind(new Date()) === 'Date'

Funkcje

kind(function(){}) === 'Function'
kind(new Function("console.log(arguments)")) === 'Function'
kind(Math.sin) === 'Function'

nieokreślony

kind(undefined) === 'undefined'

zero

kind(null) === 'null'

5

Za pomocą instanceofoperatora można sprawdzić, czy obiekt jest instancją innego obiektu, ale ponieważ nie ma klas, nie można uzyskać nazwy klasy.


Chociaż prawdą jest, że JavaScript nie ma klas jako konstruktów językowych, ogólna konwencja jest nadal taka, że ​​typ obiektu nazywa się klasą.
Saul

2
@greg Pewnie, ale instanceofsprawdza tylko, czy obiekt dziedziczy po innych obiektach. Np. Prosty []dziedziczy po Array, ale Array dziedziczy również po Object. Ponieważ większość obiektów ma wiele poziomów dziedziczenia, znalezienie najbliższego prototypu jest lepszą techniką. Zobacz moją odpowiedź, jak to zrobić.
mikemaccana

4

Oto implementacja oparta na zaakceptowanej odpowiedzi :

/**
 * Returns the name of an object's type.
 *
 * If the input is undefined, returns "Undefined".
 * If the input is null, returns "Null".
 * If the input is a boolean, returns "Boolean".
 * If the input is a number, returns "Number".
 * If the input is a string, returns "String".
 * If the input is a named function or a class constructor, returns "Function".
 * If the input is an anonymous function, returns "AnonymousFunction".
 * If the input is an arrow function, returns "ArrowFunction".
 * If the input is a class instance, returns "Object".
 *
 * @param {Object} object an object
 * @return {String} the name of the object's class
 * @see <a href="https://stackoverflow.com/a/332429/14731">https://stackoverflow.com/a/332429/14731</a>
 * @see getFunctionName
 * @see getObjectClass 
 */
function getTypeName(object)
{
  const objectToString = Object.prototype.toString.call(object).slice(8, -1);
  if (objectToString === "Function")
  {
    const instanceToString = object.toString();
    if (instanceToString.indexOf(" => ") != -1)
      return "ArrowFunction";
    const getFunctionName = /^function ([^(]+)\(/;
    const match = instanceToString.match(getFunctionName);
    if (match === null)
      return "AnonymousFunction";
    return "Function";
  }
  // Built-in types (e.g. String) or class instances
  return objectToString;
};

/**
 * Returns the name of a function.
 *
 * If the input is an anonymous function, returns "".
 * If the input is an arrow function, returns "=>".
 *
 * @param {Function} fn a function
 * @return {String} the name of the function
 * @throws {TypeError} if {@code fn} is not a function
 * @see getTypeName
 */
function getFunctionName(fn)
{
  try
  {
    const instanceToString = fn.toString();
    if (instanceToString.indexOf(" => ") != -1)
      return "=>";
    const getFunctionName = /^function ([^(]+)\(/;
    const match = instanceToString.match(getFunctionName);
    if (match === null)
    {
      const objectToString = Object.prototype.toString.call(fn).slice(8, -1);
      if (objectToString === "Function")
        return "";
      throw TypeError("object must be a Function.\n" +
        "Actual: " + getTypeName(fn));
    }
    return match[1];
  }
  catch (e)
  {
    throw TypeError("object must be a Function.\n" +
      "Actual: " + getTypeName(fn));
  }
};

/**
 * @param {Object} object an object
 * @return {String} the name of the object's class
 * @throws {TypeError} if {@code object} is not an Object
 * @see getTypeName
 */
function getObjectClass(object)
{
  const getFunctionName = /^function ([^(]+)\(/;
  const result = object.constructor.toString().match(getFunctionName)[1];
  if (result === "Function")
  {
    throw TypeError("object must be an Object.\n" +
      "Actual: " + getTypeName(object));
  }
  return result;
};


function UserFunction()
{
}

function UserClass()
{
}

let anonymousFunction = function()
{
};

let arrowFunction = i => i + 1;

console.log("getTypeName(undefined): " + getTypeName(undefined));
console.log("getTypeName(null): " + getTypeName(null));
console.log("getTypeName(true): " + getTypeName(true));
console.log("getTypeName(5): " + getTypeName(5));
console.log("getTypeName(\"text\"): " + getTypeName("text"));
console.log("getTypeName(userFunction): " + getTypeName(UserFunction));
console.log("getFunctionName(userFunction): " + getFunctionName(UserFunction));
console.log("getTypeName(anonymousFunction): " + getTypeName(anonymousFunction));
console.log("getFunctionName(anonymousFunction): " + getFunctionName(anonymousFunction));
console.log("getTypeName(arrowFunction): " + getTypeName(arrowFunction));
console.log("getFunctionName(arrowFunction): " + getFunctionName(arrowFunction));
//console.log("getFunctionName(userClass): " + getFunctionName(new UserClass()));
console.log("getTypeName(userClass): " + getTypeName(new UserClass()));
console.log("getObjectClass(userClass): " + getObjectClass(new UserClass()));
//console.log("getObjectClass(userFunction): " + getObjectClass(UserFunction));
//console.log("getObjectClass(userFunction): " + getObjectClass(anonymousFunction));
//console.log("getObjectClass(arrowFunction): " + getObjectClass(arrowFunction));
console.log("getTypeName(nativeObject): " + getTypeName(navigator.mediaDevices.getUserMedia));
console.log("getFunctionName(nativeObject): " + getFunctionName(navigator.mediaDevices.getUserMedia));

Używamy właściwości konstruktora tylko wtedy, gdy nie mamy innego wyboru.


3

Za pomocą operatora „instanceof” można ustalić, czy obiekt jest instancją określonej klasy, czy nie. Jeśli nie znasz nazwy typu obiektu, możesz użyć jego właściwości konstruktora. Właściwość konstruktora obiektów, jest odwołaniem do funkcji, która jest używana do ich inicjowania. Przykład:

function Circle (x,y,radius) {
    this._x = x;
    this._y = y;
    this._radius = raduius;
}
var c1 = new Circle(10,20,5);

Teraz c1.constructor jest odwołaniem do Circle()funkcji. Możesz także użyć typeofoperatora, ale typeofoperator pokazuje ograniczone informacje. Jednym z rozwiązań jest użycie toString()metody obiektu globalnego Object. Na przykład, jeśli masz obiekt, powiedzmy myObject, możesz użyć toString()metody obiektu globalnego, aby określić typ klasy myObject. Użyj tego:

Object.prototype.toString.apply(myObject);

3

Powiedz, że masz var obj;

Jeśli chcesz tylko nazwę typu obj, na przykład „Object”, „Array” lub „String”, możesz użyć tego:

Object.prototype.toString.call(obj).split(' ')[1].replace(']', '');

2

Najbliżej można dostać typeof, ale zwraca tylko „obiekt” dla dowolnego rodzaju niestandardowego. Dla tych, patrz Jason Bunting .

Edytuj, Jason z jakiegoś powodu usunął swój post, więc po prostu użyj constructorwłaściwości Object .


Tak, przepraszam - usunąłem go, ponieważ uznałem, że instanceof () jest lepszym sposobem na zrobienie czegoś, ale po prostu usunąłem go, aby mógł służyć jako odniesienie.
Jason Bunting,

2
Niezbyt doskonałe odpowiedzi są nadal przydatne, choćby dla innych, aby później odpowiedzieć na to pytanie, ponieważ mają podobny problem. Więc naprawdę nie powinieneś ich usuwać. Zapisz usuwa niepoprawne odpowiedzi.
sblundy

2
Tak, wiem - głosisz chór, powiedziałem dokładnie to samo innym. Życie z rzeczami, o których wiemy, że są prawdziwe, jest często trudniejsze niż się wydaje. :)
Jason Bunting,

0

Jeśli ktoś szukał rozwiązania współpracującego z jQuery, oto dostosowany kod wiki (oryginalny łamie jQuery).

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

Tak, jQuery nie wykonuje testu „hasOwnProperty”, dlatego wylicza getNamei przewraca się.
nicodemus13,

0

Lodash ma wiele metod isMethod, więc jeśli używasz Lodash, być może taka mieszanka może być przydatna:

  // Mixin for identifying a Javascript Object

  _.mixin({
      'identify' : function(object) {
        var output;
          var isMethods = ['isArguments', 'isArray', 'isArguments', 'isBoolean', 'isDate', 'isArguments', 
              'isElement', 'isError', 'isFunction', 'isNaN', 'isNull', 'isNumber', 
              'isPlainObject', 'isRegExp', 'isString', 'isTypedArray', 'isUndefined', 'isEmpty', 'isObject']

          this.each(isMethods, function (method) {
              if (this[method](object)) {
                output = method;
                return false;
              }
          }.bind(this));
      return output;
      }
  });

Dodaje metodę lodash o nazwie „identyfikuj”, która działa w następujący sposób:

console.log(_.identify('hello friend'));       // isString

Plunker: http://plnkr.co/edit/Zdr0KDtQt76Ul3KTEDSN


0

Ok, ludzie powoli budowałem metodę catch all dla tego przez kilka lat lol! Sztuką jest:

  1. Mają mechanizm tworzenia klas.
  2. Mieć mechanizm sprawdzania wszystkich klas, prymitywów i wartości utworzonych / wygenerowanych przez rodzimych konstruktorów.
  3. Mają mechanizm rozszerzania klas utworzonych przez użytkownika na nowe, dzięki czemu powyższa funkcjonalność przenika przez Twój kod / aplikację / bibliotekę itp.

Na przykład (lub aby zobaczyć, jak poradziłem sobie z problemem) spójrz na następujący kod na github: https://github.com/elycruz/sjljs/blob/master/src/sjl/sjl.js i wyszukaj:

classOf =, classOfIs =i lub lub defineSubClass =(bez tyknięć (`)).

Jak widać, mam pewne mechanizmy, które zmuszają classOfmnie do nadania mi nazwy typu klas / konstruktorów, niezależnie od tego, czy jest to prymitywna, klasa zdefiniowana przez użytkownika, wartość utworzona przy użyciu natywnego konstruktora, Null, NaN itp. Dla każdej wartości javascript otrzymam unikalną nazwę typu od classOffunkcji. Ponadto mogę przekazać rzeczywiste konstruktory, sjl.classOfIsaby sprawdzić typ wartości, oprócz możliwości przekazania także nazwy typu! Na przykład:

`` // Proszę wybaczyć długie przestrzenie nazw! Nie miałem pojęcia o wpływie, dopóki nie użyłem ich przez chwilę (ssą haha)

var SomeCustomClass = sjl.package.stdlib.Extendable.extend({
    constructor: function SomeCustomClass () {},
    // ...
}),

HelloIterator = sjl.ns.stdlib.Iterator.extend( 
    function HelloIterator () {}, 
    { /* ... methods here ... */ },
    { /* ... static props/methods here ... */ }
),

helloIt = new HelloIterator();

sjl.classOfIs(new SomeCustomClass(), SomeCustomClass) === true; // `true`
sjl.classOfIs(helloIt, HelloIterator) === true; // `true`

var someString = 'helloworld';

sjl.classOfIs(someString, String) === true; // `true`

sjl.classOfIs(99, Number) === true; // true

sjl.classOf(NaN) === 'NaN'; // true

sjl.classOf(new Map()) === 'Map';
sjl.classOf(new Set()) === 'Set';
sjl.classOfIs([1, 2, 4], Array) === true; // `true`

// etc..

// Also optionally the type you want to check against could be the type's name
sjl.classOfIs(['a', 'b', 'c'], 'Array') === true; // `true`!
sjl.classOfIs(helloIt, 'HelloIterator') === true; // `true`!

``

Jeśli chcesz przeczytać więcej o tym, jak korzystam z wyżej wymienionej konfiguracji, zapoznaj się z repozytorium: https://github.com/elycruz/sjljs

Również książki z treścią na ten temat: - „Wzory JavaScript” Stoyana Stefanova. - „Javascript - przewodnik definitywny”. David Flanagan. - i wiele innych .. (szukaj w Internecie).

Możesz także szybko przetestować funkcje, o których tu mówię: - http://sjljs.elycruz.com/0.5.18/tests/for-browser/ (również ścieżka 0.5.18 w adresie URL zawiera źródła z github tam minus moduły node_moduły i tak dalej).

Happy Coding!


0

Dość proste!

  • Moja ulubiona metoda na zdobycie czegokolwiek w JS
function getType(entity){
    var x = Object.prototype.toString.call(entity)
    return x.split(" ")[1].split(']')[0].toLowerCase()
}
  • moja ulubiona metoda sprawdzania typu czegokolwiek w JS
function checkType(entity, type){
    return getType(entity) === type
}

-1

Zastosowanie class.name. Działa to również z function.name.

class TestA {}
console.log(TestA.name); // "TestA"

function TestB() {}
console.log(TestB.name); // "TestB"

Pytanie wyraźnie mówi, classa nie instancja.
qwertzguy,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.