Самый точный способ проверить тип объекта JS?


138

typeofОператор не помогает нам найти реальный тип объекта.

Я уже видел следующий код:

Object.prototype.toString.apply(t)  

Вопрос:

Это самый точный способ проверки типа объекта?


2
Посмотрите на эту статью: javascriptweblog.wordpress.com/2011/08/08/…
— Джеймс Аллардис

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]
— Крис Марисик

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.
— solstice333

3
Возникает вопрос: почему они не обернули этот код более удобным методом или не позволили скомпилировать дополнительный оператор? Я знаю, что вы всего лишь посланник, но, честно говоря, это ужасно.
— Андрей С

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

Хорошее решение. Я заимствую твою функцию в мою библиотеку :)
— Донг Нгуен

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?
— Джейми Treworgy

@jamietre потому что "foo" instanceof Stringломается
— Райнос

ОК, так что "typeof (o) === 'object' && o instanceof SomeObject". Это легко проверить на строки. Просто кажется дополнительной работой, без решения основной проблемы необходимости заранее знать, для чего вы тестируете.
— Джейми Треворги

Извините, что фрагмент кода не имеет смысла, но я думаю, вы знаете, что я имею в виду, если вы проверяете строки, тогда используйте typeof(x)==='string'вместо этого.
— Джейми Treworgy

Кстати, 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

Лучший способ узнать тип объекта REAL (включая ОБА собственное имя объекта или типа данных (например, String, Date, Number, ..etc) И РЕАЛЬНЫЙ тип объекта (даже пользовательский); это захват свойство name конструктора прототипа объекта:

Родной Тип 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
— Джулиан Найт
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.