Есть ли в 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
полезен, важно, но в настоящее время я не чувствую, что это очень важно для этой дискуссии. Мой разум открыт для изменения, хотя. :)