Лучший способ получить тип переменной Javascript?

260

Есть ли лучший способ получить тип переменной в JS, чем typeof? Он отлично работает, когда вы делаете:

> typeof 1
"number"
> typeof "hello"
"string"

Но это бесполезно, когда вы пытаетесь:

> typeof [1,2]
"object"
>r = new RegExp(/./)
/./
> typeof r
"function"

Я знаю instanceof, но это требует, чтобы вы знали тип заранее.

> [1,2] instanceof Array
true
> r instanceof RegExp
true

Есть ли способ лучше?

Aillyn
источник
1
К вашему сведению, typeof new RegExp(/./); // "function"проблема в Chrome, по-видимому, исправлена ​​в Chrome 14.
user113716
Предупреждение: если вы минимизируете свой JS и используете «пользовательские объекты», такие как классы машинописи, то некоторые ответы ниже приведут к получению запутанного имени функции, например, nвместо ожидаемого исходного имени. Например, constructor.nameможет дать вам nвместо ожидаемого полного имени
Simon_Weaver

Ответы:

222

Ангус Кролл недавно написал интересную запись в блоге об этом -

http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/

Он рассматривает все «за» и «против» различных методов, а затем определяет новый метод «toType» -

var toType = function(obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
ipr101
источник
3
Интересное примечание: здесь может произойти сбой при передаче объекту «хоста» функции. ActiveXObjectЭкземпляры Internet Explorer , например.
Энди Э.
Если вы создали свой собственный тип объекта (наследование прототипа), как вы могли бы заставить его возвращать более конкретное значение, чем «объект»? Это также поднималось как проблема здесь , но никогда не рассматривалось.
EleventyOne
1
Если ваш тип именования содержит: или _, вы можете расширить это регулярное выражение доmatch(/\s([a-z,_,:,A-Z]+)/)
masi
6
Регулярное выражение также должно включать числа для таких вещей, как Uint8Array. Вместо этого рассмотрим более общий, match(/\s([^\]]+)/)который должен обрабатывать что угодно.
Лили Финли
2
Я бы предложил \wвместо этого использовать слово оператор ...
Кайзер
51

Вы можете попробовать использовать constructor.name.

[].constructor.name
new RegExp().constructor.name

Как и во всем JavaScript, кто-то в конце концов неизменно укажет, что это как-то зло, так что здесь есть ссылка на ответ, который довольно хорошо это освещает.

Альтернативой является использование Object.prototype.toString.call

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)
Алекс Турпин
источник
10
nameявляется расширением ECMAScript и не будет работать во всех браузерах.
Энди E
16
Ответ проголосовал за подтверждение подозрения, что все, что сделано в javascript, является каким-то злым.
Liljoshu
1
ЗЛО: если вы минимизируете свой код, то, constructor.nameскорее всего, в конечном итоге вы получите что-то похожее nна ожидаемое вами имя объекта. Так что следите за этим - особенно если вы только минимизируете производство.
Simon_Weaver
nullи, undefinedк сожалению, нет конструктора.
Эндрю Гримм
Сам по себе JavaScript - это зло. И глупо. Тот факт, что на этот вопрос нет хорошего ответа, является живым доказательством этого.
user2173353
38

Достаточно хорошая функция захвата типов - та, что используется YUI3 :

var TYPES = {
    'undefined'        : 'undefined',
    'number'           : 'number',
    'boolean'          : 'boolean',
    'string'           : 'string',
    '[object Function]': 'function',
    '[object RegExp]'  : 'regexp',
    '[object Array]'   : 'array',
    '[object Date]'    : 'date',
    '[object Error]'   : 'error'
},
TOSTRING = Object.prototype.toString;

function type(o) {
    return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? 'object' : 'null');
};

Это захватывает многие из примитивов, предоставленных javascript, но вы всегда можете добавить больше, изменив TYPESобъект. Обратите внимание, что typeof HTMLElementCollectionв Safari сообщит function, но тип (HTMLElementCollection) вернетobject

Ник Хушер
источник
31

Вам может пригодиться следующая функция:

function typeOf(obj) {
  return {}.toString.call(obj).split(' ')[1].slice(0, -1).toLowerCase();
}

Или в ES7 (прокомментируйте, если дальнейшие улучшения)

function typeOf(obj) {
  const { toString } = Object.prototype;
  const stringified = obj::toString();
  const type = stringified.split(' ')[1].slice(0, -1);

  return type.toLowerCase();
}

Полученные результаты:

typeOf(); //undefined
typeOf(null); //null
typeOf(NaN); //number
typeOf(5); //number
typeOf({}); //object
typeOf([]); //array
typeOf(''); //string
typeOf(function () {}); //function
typeOf(/a/) //regexp
typeOf(new Date()) //date
typeOf(new Error) //error
typeOf(Promise.resolve()) //promise
typeOf(function *() {}) //generatorfunction
typeOf(new WeakMap()) //weakmap
typeOf(new Map()) //map

Спасибо @johnrees за сообщение об ошибке, обещании, функции генератора

Vix
источник
Спасибо за это. Это также работает для объектов даты:typeOf(new Date())
Джеймс Ирвин
Ах, я забыл об этом - спасибо, теперь оно включено.
Vix
1
ИtypeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
Джонни
Я не могу заставить этот ответ работать в машинописи. Выдает ошибку:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK
Не уверен насчет TypeScript. Вы пробовали версию ES7? toString, не объявленная явно, может быть причиной?
Vix
15

Также мы можем немного изменить пример с ipr101

Object.prototype.toType = function() {
  return ({}).toString.call(this).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}

и позвонить как

"aaa".toType(); // 'string'
greymaster
источник
6
Помните, дети, манипулирование базовыми объектами JavaScript может привести к совместимости и другим странным проблемам. Попробуйте использовать это экономно в обеих библиотеках и крупных проектах!
Александр Краггс
9

однострочная функция:

function type(obj) {
    return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/,"$1").toLowerCase()
}

это дает тот же результат, что и jQuery.type()

Yukulélé
источник
3

Вы можете подать заявку Object.prototype.toStringна любой объект:

var toString = Object.prototype.toString;

console.log(toString.call([]));
//-> [object Array]

console.log(toString.call(/reg/g));
//-> [object RegExp]

console.log(toString.call({}));
//-> [object Object]

Это хорошо работает во всех браузерах, за исключением IE - при вызове этого для переменной, полученной из другого окна, он просто выплевывает [object Object].

Энди Э
источник
1
@ Xeon Я думаю, что ненависть к IE слишком велика. IE9 - намного лучший браузер, чем его предшественники.
Эйлин
4
@pessimopoppotamus, но никто, кто обновит свой браузер до IE9, не использует IE.
Алекс Турпин
1
IE 9 - это шаг в правильном направлении, IE 10 выглядит довольно хорошо. Кстати, ошибка, о которой я упоминал, была регрессией в IE 9 - они исправили ее в IE 8.
Andy E
3

Мои 2 ¢! На самом деле, одна из причин, по которой я привожу это здесь, несмотря на длинный список ответов, заключается в том, чтобы предоставить немного больше all in oneтиповых решений и получить обратную связь в будущем о том, как расширить его, чтобы включить больше real types.

С помощью следующего решения, как было упомянуто выше, я объединил пару найденных здесь решений, а также включил исправление для возврата значения в jQueryопределенный объект jQuery, если он доступен . Я также добавляю метод к собственному прототипу Object. Я знаю, что это часто табу, так как это может помешать другим подобным расширениям, но я оставляю это user beware. Если вам не нравится этот способ, просто скопируйте базовую функцию куда угодно и замените все переменные thisна параметр аргумента для передачи (например, arguments [0]).

;(function() {  //  Object.realType
    function realType(toLower) {
        var r = typeof this;
        try {
            if (window.hasOwnProperty('jQuery') && this.constructor && this.constructor == jQuery) r = 'jQuery';
            else r = this.constructor && this.constructor.name ? this.constructor.name : Object.prototype.toString.call(this).slice(8, -1);
        }
        catch(e) { if (this['toString']) r = this.toString().slice(8, -1); }
        return !toLower ? r : r.toLowerCase();
    }
    Object['defineProperty'] && !Object.prototype.hasOwnProperty('realType')
        ? Object.defineProperty(Object.prototype, 'realType', { value: realType }) : Object.prototype['realType'] = realType;
})();

Тогда просто используйте с легкостью, вот так:

obj.realType()  //  would return 'Object'
obj.realType(true)  //  would return 'object'

Примечание. Имеется 1 аргумент. Если это bool of true, то возврат всегда будет в нижнем регистре .

Больше примеров:

true.realType();                            //  "Boolean"
var a = 4; a.realType();                    //  "Number"
$('div:first').realType();                   // "jQuery"
document.createElement('div').realType()    //  "HTMLDivElement"

Если у вас есть что добавить, что может быть полезным, например, определение того, когда объект был создан с другой библиотекой (Moo, Proto, Yui, Dojo и т. Д.), Пожалуйста, не стесняйтесь комментировать или редактировать это, и пусть оно будет более точный и точный. ИЛИ перевернись к тому, что GitHubя сделал для этого, и дай мне знать. Там вы также найдете быструю ссылку на файл cdn min.

SpYk3HH
источник
2
function getType(obj) {
    if(obj && obj.constructor && obj.constructor.name) {
        return obj.constructor.name;
    }
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

В моих предварительных тестах это работает довольно хорошо. В первом случае будет напечатано имя любого объекта, созданного с помощью «new», а во втором случае должно быть зафиксировано все остальное.

Я использую, (8, -1)потому что я предполагаю, что результат всегда будет начинаться с [objectи заканчиваться, ]но я не уверен, что это верно в каждом сценарии.

mpen
источник
2

Думаю, самое универсальное решение здесь - это сначала проверить, undefinedа nullпотом просто позвонить constructor.name.toLowerCase().

const getType = v =>
  v === undefined
    ? 'undefined'
    : v === null
      ? 'null'
      : v.constructor.name.toLowerCase();




console.log(getType(undefined)); // 'undefined'
console.log(getType(null)); // 'null'
console.log(getType('')); // 'string'
console.log(getType([])); // 'array'
console.log(getType({})); // 'object'
console.log(getType(new Set())); // `set'
console.log(getType(Promise.resolve())); // `promise'
console.log(getType(new Map())); // `map'
Arsenowitch
источник
Насколько я знаю - .constructor.nameвсегда содержит строковое значение. Для undefined / null у нас есть соответствующие строки, возвращаемые функцией.
Арсенович
Спасибо - удаляю мои комментарии…
Берги
Поскольку constructorэто унаследованное свойство, оно разбивается на объекты, созданные с помощью Object.create(null). Достаточно просто исправить, но как это назвать? "[объект нуль]"?
traktor53
0

typeof условие используется для проверки типа переменной, если вы проверяете тип переменной в условии if-else, например

if(typeof Varaible_Name "undefined")
{

}
MzkZeeshan
источник