Получить название типа объекта - PullRequest
1131 голосов
/ 02 декабря 2008

Есть ли в JavaScript эквивалент Java class.getName()?

Ответы [ 19 ]

1484 голосов
/ 02 декабря 2008

Есть ли в JavaScript эквивалент Java class.getName()?

Нет .

ES2015 Обновление : имя class Foo {} равно Foo.name. Имя класса thing, независимо от типа thing, равно thing.constructor.name. Встроенные конструкторы в среде ES2015 имеют правильное свойство name; например, (2).constructor.name - это "Number".


Но вот различные хаки, которые все так или иначе падают:

Вот хак, который сделает то, что вам нужно - помните, что он модифицирует прототип Объекта, что люди не одобряют (обычно по уважительной причине)

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

Теперь у всех ваших объектов будет функция getName(), которая будет возвращать имя конструктора в виде строки. Я проверял это в FF3 и IE7, я не могу говорить о других реализациях.

Если вы не хотите этого делать, здесь обсуждаются различные способы определения типов в JavaScript ...


Я недавно обновил это, чтобы быть немного более исчерпывающим, хотя это вряд ли. Исправления приветствуются ...

Использование свойства constructor ...

Каждый object имеет значение для своего свойства constructor, но в зависимости от того, как было построено object, а также от того, что вы хотите сделать с этим значением, оно может или не может быть полезным.

Вообще говоря, вы можете использовать свойство constructor для проверки типа объекта следующим образом:

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

Итак, это работает достаточно хорошо для большинства нужд. Это сказал ...

Предостережения

Не будет работать ВСЕ во многих случаях

Этот паттерн, хотя и нарушен, довольно распространен:

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

Objects, построенный через new Thingy, будет иметь свойство constructor, которое указывает на Object, а не Thingy. Таким образом, мы падаем с самого начала; вы просто не можете доверять constructor в кодовой базе, которую вы не контролируете.

Множественное наследование

Примером, где это не так очевидно, является использование множественного наследования:

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

Вещи теперь не работают так, как вы могли бы ожидать:

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

Таким образом, вы можете получить неожиданные результаты, если object вашего тестирования имеет другой object, установленный как prototype. Есть способы обойти это за рамками этого обсуждения.

Существуют и другие варианты использования свойства constructor, некоторые из которых интересны, другие - не очень; сейчас мы не будем углубляться в это использование, поскольку оно не имеет отношения к этому обсуждению.

Не будет работать рамка и кросс-окно

Использование .constructor для проверки типа прекратится, когда вы захотите проверить тип объектов, поступающих из различных window объектов, например, из iframe или всплывающего окна. Это связано с тем, что в каждом «окне» есть разные версии каждого типа ядра constructor, то есть

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

Использование оператора instanceof ...

Оператор instanceof также является чистым способом проверки типа object, но имеет свои потенциальные проблемы, как и свойство constructor.

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

Но instanceof не работает для литеральных значений (потому что литералы не Objects)

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

Литералы должны быть обернуты в Object, чтобы instanceof работал, например

new Number(3) instanceof Number // true

Проверка .constructor отлично работает для литералов, потому что вызов метода . неявно оборачивает литералы в соответствующие им типы объектов

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

Почему две точки для 3? Потому что Javascript интерпретирует первую точку как десятичную точку;)

Не будет работать кросс-рамка и кросс-окно

instanceof также не будет работать в разных окнах по той же причине, что и проверка свойства constructor.


Использование свойства name свойства constructor ...

Не работает ВСЕ во многих случаях

Опять же, смотрите выше; constructor весьма распространено, чтобы быть совершенно и совершенно неправильным и бесполезным.

НЕ работает в Использование myObjectInstance.constructor.name даст вам строку, содержащую имя используемой функции constructor, но с учетом предостережений относительно свойства constructor, которые были упомянуты ранее. Для IE9 и выше, вы можете monkey-patch в поддержке : 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) {} }); } Обновленная версия из рассматриваемой статьи. Это было добавлено через 3 месяца после публикации статьи, это рекомендуемая версия для использования автором статьи Мэтью Шарли. Это изменение было вдохновлено комментариями , указывающими на возможные подводные камни в предыдущем коде. 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) {} }); } Использование Object.prototype.toString Получается, что подробности этого сообщения , вы можете использовать Object.prototype.toString - низкоуровневую и универсальную реализацию toString - чтобы получить тип для всех встроенных типов Object.prototype.toString.call('abc') // [object String] Object.prototype.toString.call(/abc/) // [object RegExp] Object.prototype.toString.call([1,2,3]) // [object Array] Можно написать короткую вспомогательную функцию, например function type(obj){ return Object.prototype.toString.call(obj).slice(8, -1); } чтобы убрать фуфло и получить только имя типа type('abc') // String Однако он вернет Object для всех пользовательских типов. Предостережения для всех ... Все это связано с одной потенциальной проблемой, и это вопрос о том, как был создан рассматриваемый объект. Вот различные способы построения объектов и значений, которые будут возвращать различные методы проверки типов: // 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 Хотя в этом наборе примеров присутствуют не все перестановки, надеюсь, их достаточно, чтобы дать вам представление о том, как могут возникать беспорядочные вещи в зависимости от ваших потребностей. Ничего не предполагайте, если вы точно не понимаете, что вам нужно, вы можете закончить взломом кода там, где вы этого не ожидаете, из-за отсутствия уловок тонкостей. Примечание:

Обсуждение оператора typeof может показаться явным упущением, но на самом деле бесполезно помогать определить, является ли object заданным типом, поскольку он очень упрощен. Понимание того, где typeof полезен, важно, но в настоящее время я не чувствую, что это очень важно для этой дискуссии. Мой разум открыт для изменения, хотя. :)

112 голосов
/ 02 декабря 2008

Ответ Джейсона Бантинга дал мне достаточно подсказки, чтобы найти то, что мне нужно:

<<Object instance>>.constructor.name

Так, например, в следующем фрагменте кода:

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

myInstance.constructor.name вернется "MyObject".

25 голосов
/ 17 июня 2011

Небольшой трюк, которым я пользуюсь:

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

var MySquare = new Square();
console.log(MySquare.className); // "Square"
17 голосов
/ 28 августа 2011

Обновление

Если быть точным, я думаю, что OP запросил функцию, которая извлекает имя конструктора для конкретного объекта. С точки зрения Javascript, object не имеет типа, но является типом и само по себе . Однако разные объекты могут иметь разные конструкторы .

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"


Примечание: приведенный ниже пример устарел.

A сообщение в блоге , связанное с Christian Sciberras содержит хороший пример того, как это сделать. А именно, путем расширения прототипа объекта:

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
12 голосов
/ 24 марта 2012

Использование Object.prototype.toString

Оказывается, что в этом сообщении вы можете использовать Object.prototype.toString - низкоуровневую и универсальную реализацию toString - чтобы получить тип для всех встроенных типов

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

Можно написать короткую вспомогательную функцию, например

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 голосов
/ 05 марта 2012

Вот решение, которое я придумала, которое решает недостатки instanceof. Он может проверять типы объектов из перекрестных окон и перекрестных рамок и не имеет проблем с примитивными типами.

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 требует два параметра: объект и тип. Реальная хитрость в том, как это работает, заключается в том, что он проверяет, находится ли объект из того же окна, и если нет, получает окно объекта.

Примеры:

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

Аргумент типа также может быть функцией обратного вызова, которая возвращает конструктор. Функция обратного вызова получит один параметр, который является окном предоставленного объекта.

Примеры:

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

Следует иметь в виду, что IE <9 не предоставляет конструктор для всех объектов, поэтому приведенный выше тест для NodeList вернул бы false, а также isInstance (alert, "Function") вернул бы false. </p>

7 голосов
/ 07 декабря 2013

Я действительно искал похожую вещь и наткнулся на этот вопрос. Вот как я получаю типы: 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);
}
6 голосов
/ 23 октября 2013

Функция kind () из Agave.JS вернет:

  • ближайший прототип в дереве наследования
  • для всегда примитивных типов, таких как 'null' и 'undefined', примитивное имя.

Работает со всеми объектами и примитивами JS, независимо от того, как они были созданы , и не вызывает никаких сюрпризов Примеры:

Цифры

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'

Строка

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

Booleans

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

Массивы

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

Предметы

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

Дата

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

Функции

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

неопределенный

kind(undefined) === 'undefined'

1044 * пустой * kind(null) === 'null'

6 голосов
/ 12 августа 2011

Используйте constructor.name, когда можете, и регулярные выражения, когда я не могу.

Function.prototype.getName = function(){
  if (typeof this.name != 'undefined')
    return this.name;
  else
    return /function (.+)\(/.exec(this.toString())[1];
};
5 голосов
/ 02 декабря 2008

Вы можете использовать оператор instanceof, чтобы увидеть, является ли объект экземпляром другого, но поскольку классов нет, вы не можете получить имя класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...