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

kmatheny
источник
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"
wiky
источник
Этот тест на 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
Raynos
источник
Как это лучше / отличается от высказывания 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
beliha
источник
Это терпит неудачу, если объект или 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

Недорого и просто.

Codebeat
источник
Возвращает, 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
Джулиан Найт