Кращий спосіб отримати тип змінної 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

Чи є кращий спосіб?


1
FYI, typeof new RegExp(/./); // "function"видається , що проблема в Chrome виправлена ​​в Chrome 14.
user113716


Попередження: Якщо ви мінімізуєте свій JS і використовуєте "власні об'єкти", такі як класи шрифтів, то деякі відповіді нижче нададуть вам заплутане ім'я функції, наприклад nзамість очікуваного оригінального імені. наприклад, constructor.nameможе дати вам nзамість очікуваного повного імені
Simon_Weaver

Відповіді:


221

Нещодавно Ангус Кролл написав цікаву публікацію в цьому блозі -

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

3
Цікава сторона тут - це потенційно може зламатись, коли об’єкт "хост" передається функції. ActiveXObjectНаприклад, Internet Explorer .
Енді Е

Якщо ви створили свій власний тип об'єкта (наслідування прототипів), як би ви могли змусити його повернути більш конкретне значення, ніж "об'єкт"? Це також було порушено як тут питання , але його ніхто не вирішував.
EleventyOne

1
Якщо ваше найменування типів містить: або _ ви можете продовжити цей регулярний вираз доmatch(/\s([a-z,_,:,A-Z]+)/)
масі

6
У регулярний вираз також потрібно включити числа для таких речей Uint8Array. Натомість розглянемо більш загальне, match(/\s([^\]]+)/)що має вирішувати будь-що.
Лілі Фінлі

2
Я б запропонував використати \wслово оператор замість…
kaiser

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 і працюватиме не у всіх браузерах.
Енді Е

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 за повідомлення про: помилку, обіцянку, функцію генератора


Дякую за це Він також працює для об’єктів дати:typeOf(new Date())
Джеймс Ірвін

Ах, я забув про це - дякую, тепер це включено.
Vix

1
ІtypeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
johnrees

Я не можу отримати цю відповідь для роботи в машинописі. Помилка:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK

Не впевнений у TypeScript. Ви спробували версію ES7? toString, про який явно не заявлено, може бути причиною?
Вікс

15

Також ми можемо змінити невеликий приклад з ipr101

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

і дзвоніть як

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

6
Пам’ятайте, діти, маніпулювання базовими об’єктами JavaScript може призвести до сумісності та інших дивних проблем. Постарайтеся використовувати це економно як у бібліотеках, так і у великих проектах!
Олександр Креггс

9

функція однієї лінії:

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

це дає такий же результат, як і jQuery.type()


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.
Алекс Турпін

1
IE 9 - це крок у правильному напрямку, IE 10 виглядає досить добре. Між іншим, помилка, про яку я згадував, була регресією в IE 9 - вони виправили її в IE 8.
Енді Е

3

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

З наступним рішенням, як було сказано вище, я поєднав пару знайдених тут рішень, а також включив виправлення для повернення значення jQueryоб'єкта, визначеного jQuery, якщо він є . Я також додаю метод до нативного прототипу Object. Я знаю, що це часто табу, оскільки це може заважати іншим таким розширенням, але я залишаю це 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'

Примітка. Є один аргумент прохідний. Якщо це 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.


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", а 2-й регістр повинен зафіксувати все інше.

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


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'

Наскільки я знаю - .constructor.nameзавжди містить значення рядка. Для undefined / null у нас є відповідні рядки, повернуті функцією.
Арсенович

Спасибі - видалення моїх коментарів ...
Берги

Оскільки constructorє спадковим властивістю, це розриває для об'єктів, створених за допомогою Object.create(null). Досить просто виправити, але як це назвати? "[об'єкт null]"?
traktor53

0

typeof умова використовується для перевірки типу змінної, якщо ви перевіряєте тип змінної в умові if-else, наприклад

if(typeof Varaible_Name "undefined")
{

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.