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


есть ли лучший способ получить тип переменной в 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

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

9 224

9 ответов:

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

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()
}

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

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

как и во всем JavaScript, кто-то в конечном итоге неизменно указывает, что это как-то зло, поэтому вот ссылка на ответ это покрывает это довольно хорошо.

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

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)

достаточно хорошая функция захвата типа используется 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, но вы всегда можете добавить больше, изменив

вы можете найти следующую функцию полезности:

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 за уведомление меня: ошибка, обещание, generatorfunction

Также мы можем изменить небольшой пример из ipr101

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

и звонок как

"aaa".toType(); // 'string'

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

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

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

вы можете подать заявку 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].

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

С помощью следующего решения, как уже упоминалось выше, я объединил несколько решений, найденных здесь, а также включил исправить для возврата a значение jQuery на jQuery определенный объект при наличии. Я также добавляю метод к прототипу собственного объекта. Я знаю, что это часто табу, так как это может помешать другим таким расширениям, но я оставляю это user beware. Если вам не нравится этот способ, просто скопируйте базовую функцию в любом месте и замените все переменные this с параметром аргумента для передачи (например аргументы[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 аргумент сносно. Если это булевая переменная, отвечающая за 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.

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 и ] но я не уверен, что это истинно в каждом случае.