Найточніший спосіб перевірити тип об'єкта JS?


137

typeofОператор не допомагає нам знайти реальний тип об'єкта.

Я вже бачив такий код:

Object.prototype.toString.apply(t)  

Питання:

Дійсно це самий точний спосіб перевірки типу об'єкта?



4
Подивіться на цей пост: stackoverflow.com/questions/332422 / ...
isJustMe


3
Найбільш точний спосіб - це не тестування типу. Для чого потрібні типи?
hugomg

Object.prototype.toString.call / Object.prototype.toString.apply
xgqfrms

Відповіді:


191

Специфікація JavaScript дає точно один правильний спосіб визначення класу об'єкта:

Object.prototype.toString.call(t);

http://bonsaiden.github.com/JavaScript-Garden/#types


5
Якщо ви шукаєте конкретний тип, ви, ймовірно, захочете щось зробити за принципом: Object.prototype.toString.call(new FormData()) === "[object FormData]"що було б правдою. Ви також можете використати slice(8, -1)для повернення FormDataзамість[object FormData]
Chris Marisic

4
Чи є різниця між використанням Object.prototypeта {}?
GetFree

3
можливо, це змінилося за ці роки, але Object.prototype.toString.call(new MyCustomObject())повертається, [object Object]тоді як new MyCustomObject() instanceOf MyCustomObject returns trueце те, що я хотів (Chrome 54.0.2840.99 м)
Маслоу

@Maslow, я зіткнувся з тією ж проблемою, яку ви порушили. Переглянувши деяку документацію в Інтернеті, я закінчила використання new MyCustomObject().constructor === MyCustomObject.
сонцестояння333

3
Він задає питання, чому вони не перетворили цей код у більш зручний спосіб, або дозволили додатковий оператор компілювати для цього. Я знаю, що ти лише посланник, але, відверто кажучи, це жахливо.
Andrew S

60

Object.prototype.toStringце хороший спосіб, але його продуктивність є гіршим.

http://jsperf.com/check-js-type

перевірити продуктивність js

Використовуйте typeofдля вирішення якоїсь основної проблеми (String, Number, Boolean ...) і використовуйте Object.prototype.toStringдля вирішення чогось складного (наприклад, Array, Date, RegExp).

і це моє рішення:

var type = (function(global) {
    var cache = {};
    return function(obj) {
        var key;
        return obj === null ? 'null' // null
            : obj === global ? 'global' // window in browser or global in nodejs
            : (key = typeof obj) !== 'object' ? key // basic: string, boolean, number, undefined, function
            : obj.nodeType ? 'object' // DOM element
            : cache[key = ({}).toString.call(obj)] // cached. date, regexp, error, object, array, math
            || (cache[key] = key.slice(8, -1).toLowerCase()); // get XXXX from [object XXXX], and cache it
    };
}(this));

використовувати як:

type(function(){}); // -> "function"
type([1, 2, 3]); // -> "array"
type(new Date()); // -> "date"
type({}); // -> "object"

Цей тест на jsPerf не зовсім точний. Ці тести не рівні (тестування на одне і те ж). Наприклад, typeof [] повертає "об'єкт", typeof {} також повертає "об'єкт", навіть якщо один є об'єктом масиву, а інший - об'єктом "об'єкт". З цим тестом є багато інших проблем ... Будьте уважні, дивлячись на jsPerf, що тести порівнюють яблука з яблуками.
kmatheny

Ваша typeфункція хороша, але подивіться, як вона працює порівняно з деякими іншими typeфункціями. http://jsperf.com/code-type-test-a-test
Progo

18
Ці показники ефективності повинні бути загартовані певним здоровим глуздом. Звичайно, prototype.toString повільніше, ніж інші, на порядок, але в грандіозній схемі речей це в середньому займає пару сотень наносекунд на виклик. Якщо цей виклик не використовується в критичному шляху, який дуже часто виконується, це, мабуть, нешкідливо. Я вважаю за краще прямий код, ніж код, який швидше закінчує одну мікросекунду.
Девід

({}).toString.call(obj)повільніше, ніж Object.prototype.toString jsperf.com/object-check-test77
timaschew

Приємне рішення. Я запозичую вашу функцію у моїй лібці :)
Dong Nguyen

19

Прийнята відповідь є правильною, але мені подобається визначати цю маленьку утиліту в більшості проектів, які я будую.

var types = {
   'get': function(prop) {
      return Object.prototype.toString.call(prop);
   },
   'null': '[object Null]',
   'object': '[object Object]',
   'array': '[object Array]',
   'string': '[object String]',
   'boolean': '[object Boolean]',
   'number': '[object Number]',
   'date': '[object Date]',
}

Використовується так:

if(types.get(prop) == types.number) {

}

Якщо ви використовуєте кутовий, ви навіть можете його чисто вводити:

angular.constant('types', types);

11
var o = ...
var proto =  Object.getPrototypeOf(o);
proto === SomeThing;

Тримайте ручку на прототипі, який ви очікуєте, що об'єкт має, а потім порівняйте з ним.

наприклад

var o = "someString";
var proto =  Object.getPrototypeOf(o);
proto === String.prototype; // true

Як це краще / відрізняється, ніж говорити o instanceof String; //true?
Джеймі Треворі

@jamietre тому що "foo" instanceof Stringперерви
Raynos

Гаразд, значить, "typeof (o) === 'object' && instanceof SomeObject". Тестувати рядки досить просто. Просто здається додатковою роботою, не вирішуючи основної проблеми - заздалегідь знати, для чого ви тестуєтесь.
Джеймі Треворі

Вибачте, що фрагмент коду не має сенсу, але, я думаю, ви знаєте, що я маю на увазі, якщо ви тестуєте рядки, тоді використовуйте typeof(x)==='string'натомість.
Джеймі Треворі

BTW, Object.getPrototypeOf(true)не вдається, де (true).constructorповертається Boolean.
katspaugh

5

Я заперечую, що більшість показаних тут рішень страждають від надмірної інженерії. Мабуть, найпростіший спосіб перевірити, чи є значення типу, [object Object]це перевірити його .constructorвластивість:

function isObject (a) { return a != null && a.constructor === Object; }

або навіть коротше за допомогою стрілочних функцій:

const isObject = a => a != null && a.constructor === Object;

a != nullЧастина необхідна тому , що один може пройти в nullабо , undefinedі ви не можете отримати властивість конструктора з будь-якого з них.

Він працює з будь-яким об’єктом, створеним за допомогою:

  • Objectконструктор
  • літерали {}

Ще одна особлива особливість цього - це можливість давати правильні звіти для користувацьких класів, якими користуються Symbol.toStringTag. Наприклад:

class MimicObject {
  get [Symbol.toStringTag]() {
    return 'Object';
  }
}

Проблема тут полягає в тому, що при виклику Object.prototype.toStringйого екземпляра [object Object]буде повернуто помилковий звіт :

let fakeObj = new MimicObject();
Object.prototype.toString.call(fakeObj); // -> [object Object]

Але перевірка конструктора дає правильний результат:

let fakeObj = new MimicObject();
fakeObj.constructor === Object; // -> false

4

Найкращий спосіб з’ясувати тип РЕАЛЬНОГО об’єкта (включаючи НАРОДНУ назву об’єкта чи DataType (наприклад, String, Date, Number, ..etc)) та РЕАЛЬНИЙ тип об'єкта (навіть нестандартні); це захоплення властивість імені конструктора прототипу об'єкта:

Рідний тип Ex1:

var string1 = "Test";
console.log(string1.__proto__.constructor.name);

дисплеї:

String

Ex2:

var array1 = [];
console.log(array1.__proto__.constructor.name);

дисплеї:

Array

Спеціальні класи:

function CustomClass(){
  console.log("Custom Class Object Created!");
}
var custom1 = new CustomClass();

console.log(custom1.__proto__.constructor.name);

дисплеї:

CustomClass

Це не вдається, якщо об'єктом є nullабо undefined.
Джуліан Найт

2

Старе питання я знаю. Вам не потрібно його конвертувати. Дивіться цю функцію:

function getType( oObj )
{
    if( typeof oObj === "object" )
    {
          return ( oObj === null )?'Null':
          // Check if it is an alien object, for example created as {world:'hello'}
          ( typeof oObj.constructor !== "function" )?'Object':
          // else return object name (string)
          oObj.constructor.name;              
    }   

    // Test simple types (not constructed types)
    return ( typeof oObj === "boolean")?'Boolean':
           ( typeof oObj === "number")?'Number':
           ( typeof oObj === "string")?'String':
           ( typeof oObj === "function")?'Function':false;

}; 

Приклади:

function MyObject() {}; // Just for example

console.log( getType( new String( "hello ") )); // String
console.log( getType( new Function() );         // Function
console.log( getType( {} ));                    // Object
console.log( getType( [] ));                    // Array
console.log( getType( new MyObject() ));        // MyObject

var bTest = false,
    uAny,  // Is undefined
    fTest  function() {};

 // Non constructed standard types
console.log( getType( bTest ));                 // Boolean
console.log( getType( 1.00 ));                  // Number
console.log( getType( 2000 ));                  // Number
console.log( getType( 'hello' ));               // String
console.log( getType( "hello" ));               // String
console.log( getType( fTest ));                 // Function
console.log( getType( uAny ));                  // false, cannot produce
                                                // a string

Низька вартість і проста.


Повертається, falseякщо тестовим об’єктом є nullабоundefined
Джуліан Найт

або trueабоfalse
Джуліан Найт

@JulianKnight false все в порядку або не визначено, це нічого корисного. Тож який сенс?
Codebeat

ваш приклад повертає непослідовні дані. Одні результати - це тип даних, а інші - значення false. Як це допомагає відповісти на запитання?
Джуліан Найт

1
@JulianKnight Дивіться зміни, це те, що ви хочете? Якщо ви віддаєте перевагу невизначеному або "невизначеному" в результаті, ви можете замінити останній помилковий, якщо хочете.
Codebeat

0

Я зібрав невелику утиліту для перевірки типу, натхненну вищевказаними правильними відповідями:

thetypeof = function(name) {
        let obj = {};
        obj.object = 'object Object'
        obj.array = 'object Array'
        obj.string = 'object String'
        obj.boolean = 'object Boolean'
        obj.number = 'object Number'
        obj.type = Object.prototype.toString.call(name).slice(1, -1)
        obj.name = Object.prototype.toString.call(name).slice(8, -1)
        obj.is = (ofType) => {
            ofType = ofType.toLowerCase();
            return (obj.type === obj[ofType])? true: false
        }
        obj.isnt = (ofType) => {
            ofType = ofType.toLowerCase();
            return (obj.type !== obj[ofType])? true: false
        }
        obj.error = (ofType) => {
            throw new TypeError(`The type of ${name} is ${obj.name}: `
            +`it should be of type ${ofType}`)
        }
        return obj;
    };

приклад:

if (thetypeof(prop).isnt('String')) thetypeof(prop).error('String')
if (thetypeof(prop).is('Number')) // do something

Чи не схоже на роботу для об'єктів , які є nullабо undefinedчи trueабоfalse
Julian Knight
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.