Самый точный способ проверить тип объекта 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.