Najdokładniejszy sposób sprawdzenia typu obiektu JS?

137

typeofOperator naprawdę nie pomaga nam znaleźć prawdziwego typu obiektu.

Widziałem już następujący kod:

Object.prototype.toString.apply(t)  

Pytanie:

Czy jest to najbardziej dokładny sposób sprawdzania typu obiektu?

Royi Namir
źródło
2
Spójrz na ten artykuł: javascriptweblog.wordpress.com/2011/08/08/…
James Allardice,
4
Spójrz na ten post: stackoverflow.com/questions/332422/…
isJustMe
3
Najdokładniejszym sposobem jest ... nie testowanie typu. Dlaczego potrzebujesz typów?
hugomg
Object.prototype.toString.call / Object.prototype.toString.apply
xgqfrms

Odpowiedzi:

191

Specyfikacja JavaScript podaje dokładnie jeden właściwy sposób określenia klasy obiektu:

Object.prototype.toString.call(t);

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

kmatheny
źródło
5
Jeśli szukasz określonego typu, prawdopodobnie chciałbyś zrobić coś w stylu: Object.prototype.toString.call(new FormData()) === "[object FormData]"co byłoby prawdą. Możesz także użyć slice(8, -1)powrotu FormDatazamiast[object FormData]
Chris Marisic,
4
Czy jest jakaś różnica między używaniem Object.prototypea {}?
GetFree
3
może to się zmieniło na przestrzeni lat, ale Object.prototype.toString.call(new MyCustomObject())wraca, [object Object]a new MyCustomObject() instanceOf MyCustomObject returns trueco chciałem (Chrome 54.0.2840.99 m)
Maslow
@ Maslow, napotkałem ten sam problem, który poruszyłeś. Po przejrzeniu pewnej dokumentacji online, ostatecznie skorzystałem z new MyCustomObject().constructor === MyCustomObject.
przesilenie 333
3
Nasuwa się pytanie, dlaczego nie opakowali tego kodu w wygodniejszą metodę lub nie pozwolili na kompilację dodatkowego operatora do tego. Wiem, że jesteś tylko posłańcem, ale szczerze mówiąc, to okropne.
Andrew S
60

Object.prototype.toStringto dobry sposób, ale jego wydajność jest najgorsze.

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

sprawdź wydajność typu js

Służy typeofdo rozwiązywania podstawowych problemów (String, Number, Boolean ...) i Object.prototype.toStringdo rozwiązywania czegoś złożonego (np. Array, Date, RegExp).

a to jest moje rozwiązanie:

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));

Użyj jako:

type(function(){}); // -> "function"
type([1, 2, 3]); // -> "array"
type(new Date()); // -> "date"
type({}); // -> "object"
wiky
źródło
Ten test na jsPerf nie jest całkiem dokładny. Te testy nie są równe (testowanie tego samego). Np. Typeof [] zwraca „object”, typeof {} także zwraca „object”, nawet jeśli jeden jest obiektem Array, a drugi obiektem Object. Jest wiele innych problemów z tym testem ... Patrząc na jsPerf uważaj, że testy porównują jabłka z jabłkami.
kmatheny
Twoja typefunkcja jest dobra, ale zobacz, jak działa w porównaniu z innymi typefunkcjami. http://jsperf.com/code-type-test-a-test
Progo
18
Te wskaźniki wydajności należy łączyć ze zdrowym rozsądkiem. Jasne, prototyp.toString jest wolniejszy od innych o rząd wielkości, ale w ogólnym rozrachunku zajmuje średnio kilkaset nanosekund na wywołanie. Jeśli to wywołanie nie jest używane na ścieżce krytycznej, która jest bardzo często wykonywana, jest to prawdopodobnie nieszkodliwe. Wolałbym mieć prosty kod do przodu niż kod, który kończy się o jedną mikrosekundę szybciej.
David,
({}).toString.call(obj)jest wolniejszy niż Object.prototype.toString jsperf.com/object-check-test77
timaschew
Niezłe rozwiązanie. Pożyczam twoją funkcję do mojej biblioteki :)
Dong Nguyen
19

Zaakceptowana odpowiedź jest poprawna, ale lubię definiować to małe narzędzie w większości budowanych przeze mnie projektów.

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]',
}

Używane w ten sposób:

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

}

Jeśli używasz kątownika, możesz go nawet dokładnie wstrzyknąć:

angular.constant('types', types);
parlament
źródło
11
var o = ...
var proto =  Object.getPrototypeOf(o);
proto === SomeThing;

Trzymaj uchwyt na prototypie, którego oczekujesz od obiektu, a następnie porównaj z nim.

na przykład

var o = "someString";
var proto =  Object.getPrototypeOf(o);
proto === String.prototype; // true
Raynos
źródło
Jak to jest lepsze / inne niż mówienie o instanceof String; //true?
Jamie Treworgy
@jamietre, ponieważ "foo" instanceof Stringprzerwy
Raynos,
OK, więc "typeof (o) === 'object' && o instanceof SomeObject". Łatwo jest przetestować ciągi. Po prostu wydaje się, że jest to dodatkowa praca, bez rozwiązania podstawowego problemu, jakim jest konieczność wcześniejszego sprawdzenia, do czego testujesz.
Jamie Treworgy
Przepraszam, że fragment kodu nie ma sensu, ale myślę, że wiesz, o co mi chodzi, jeśli testujesz ciągi, użyj typeof(x)==='string'zamiast tego.
Jamie Treworgy
BTW, Object.getPrototypeOf(true)zawodzi, gdy (true).constructorzwraca Boolean.
katspaugh,
5

Twierdzę, że większość przedstawionych tutaj rozwiązań cierpi z powodu nadmiernej inżynierii. Prawdopodobnie najprostszym sposobem sprawdzenia, czy wartość jest typu, [object Object]jest sprawdzenie jej .constructorwłaściwości:

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

lub nawet krócej z funkcjami strzałkowymi:

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

a != nullJest to konieczne, ponieważ można by przejść w nullalbo undefinedi nie można wyodrębnić własność konstruktora z jednej z nich.

Działa z każdym obiektem utworzonym przez:

  • Objectkonstruktor
  • dosłowne {}

Inną fajną cechą tego narzędzia jest możliwość tworzenia poprawnych raportów dla niestandardowych klas, które używają Symbol.toStringTag. Na przykład:

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

Problem polega na tym, że podczas wywoływania Object.prototype.toStringjej instancji [object Object]zostanie zwrócony fałszywy raport :

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

Ale sprawdzenie konstruktora daje poprawny wynik:

let fakeObj = new MimicObject();
fakeObj.constructor === Object; // -> false
David
źródło
4

Najlepszym sposobem na znalezienie PRAWDZIWEGO typu obiektu (w tym ZARÓWNO natywnej nazwy obiektu lub typu danych (np. String, Date, Number, ... itd.) ORAZ RZECZYWISTEGO typu obiektu (nawet niestandardowego) jest pobranie właściwość name konstruktora prototypu obiektu:

Typ natywny Ex1:

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

wyświetlacze:

String

Ex2:

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

wyświetlacze:

Array

Klasy niestandardowe:

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

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

wyświetlacze:

CustomClass
beliha
źródło
To się nie powiedzie, jeśli obiekt jest albo nullalbo undefined.
Julian Knight
2

Stare pytanie, które znam. Nie musisz go konwertować. Zobacz tę funkcję:

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;

}; 

Przykłady:

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

Niski koszt i prosty.

Codebeat
źródło
Zwraca, falsejeśli obiekt testowy to nulllubundefined
Julian Knight
lub truelubfalse
Julian Knight
@JulianKnight false jest w porządku, ma wartość null lub undefined, nie jest to nic przydatne. Więc o co chodzi?
Codebeat
Twój przykład zwraca niespójne dane. Niektóre wyniki to typ danych, a inne to wartość false. Jak to pomaga odpowiedzieć na pytanie?
Julian Knight
1
@JulianKnight Zobacz zmiany, czy tego chcesz? Jeśli wolisz jako wynik niezdefiniowany lub „niezdefiniowany”, możesz zastąpić ostatnie fałsz, jeśli chcesz.
Codebeat
0

Przygotowałem małe narzędzie do sprawdzania typów, inspirowane powyższymi poprawnymi odpowiedziami:

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;
    };

przykład:

if (thetypeof(prop).isnt('String')) thetypeof(prop).error('String')
if (thetypeof(prop).is('Number')) // do something
Abraham Juliot
źródło
Wydaje się, że nie działa dla obiektów, które są nulllub undefinedlub truelubfalse
Julian Knight