Besserer Weg, um den Typ einer Javascript-Variablen zu erhalten?

260

Gibt es einen besseren Weg, um den Typ einer Variablen in JS zu erhalten als typeof? Es funktioniert gut, wenn Sie:

> typeof 1
"number"
> typeof "hello"
"string"

Aber es ist nutzlos, wenn Sie versuchen:

> typeof [1,2]
"object"
>r = new RegExp(/./)
/./
> typeof r
"function"

Ich weiß von instanceof, aber dazu müssen Sie den Typ vorher kennen.

> [1,2] instanceof Array
true
> r instanceof RegExp
true

Gibt es einen besseren Weg?

Aillyn
quelle
1
Zu typeof new RegExp(/./); // "function"
Ihrer Information
Warnung: Wenn Sie Ihr JS verkleinern und "benutzerdefinierte Objekte" wie Typoskriptklassen verwenden, erhalten Sie in einigen der folgenden Antworten den Namen der verschleierten Funktion, z. B. nanstelle des erwarteten ursprünglichen Namens. zB constructor.namekönnte Ihnen nanstelle des erwarteten vollständigen Namens geben
Simon_Weaver

Antworten:

221

Angus Croll hat kürzlich einen interessanten Blog-Beitrag darüber geschrieben -

http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/

Er geht die Vor- und Nachteile der verschiedenen Methoden durch und definiert dann eine neue Methode 'toType' -

var toType = function(obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
ipr101
quelle
3
Interessante Randnotiz hier - dies könnte möglicherweise brechen, wenn ein "Host" -Objekt an die Funktion übergeben wird. ActiveXObjectZum Beispiel Internet Explorer- Instanzen.
Andy E
Wenn Sie Ihren eigenen Objekttyp erstellt haben (prototypische Vererbung), wie können Sie ihn dazu bringen, einen spezifischeren Wert als "Objekt" zurückzugeben? Dies wurde auch als Frage aufgeworfen hier , wurde aber nie angesprochen.
EleventyOne
1
Wenn Ihre match(/\s([a-z,_,:,A-Z]+)/)
Typbenennung Folgendes
6
Die Regex müsste auch Zahlen für Dinge wie enthalten Uint8Array. Betrachten Sie stattdessen die allgemeineren, match(/\s([^\]]+)/)die alles behandeln sollten.
Lily Finley
2
Ich würde vorschlagen, \wstattdessen den
Wortoperator
51

Sie können versuchen, mit constructor.name.

[].constructor.name
new RegExp().constructor.name

Wie bei allem JavaScript wird irgendwann jemand ausnahmslos darauf hinweisen, dass dies irgendwie böse ist. Hier ist also ein Link zu einer Antwort , die dies ziemlich gut abdeckt.

Eine Alternative ist zu verwenden Object.prototype.toString.call

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)
Alex Turpin
quelle
10
nameist eine Erweiterung von ECMAScript und funktioniert nicht in allen Browsern.
Andy E
16
Antwort hochgestimmt, weil der Verdacht bestätigt wurde, dass alles, was in Javascript gemacht wird, irgendwie böse ist.
Liljoshu
1
BÖSE: Wenn Sie Ihren Code minimieren, constructor.namekann dies wahrscheinlich so etwas wie nder erwartete Objektname sein. Achten Sie also darauf - vor allem, wenn Sie nur in der Produktion minimieren.
Simon_Weaver
nullund habe undefinedleider keinen Konstruktor.
Andrew Grimm
JavaScript selbst ist böse. Und dumm. Die Tatsache, dass diese Frage keine gute Antwort hat, ist der lebende Beweis dafür.
user2173353
38

Eine einigermaßen gute Typerfassungsfunktion ist die von YUI3 verwendete :

var TYPES = {
    'undefined'        : 'undefined',
    'number'           : 'number',
    'boolean'          : 'boolean',
    'string'           : 'string',
    '[object Function]': 'function',
    '[object RegExp]'  : 'regexp',
    '[object Array]'   : 'array',
    '[object Date]'    : 'date',
    '[object Error]'   : 'error'
},
TOSTRING = Object.prototype.toString;

function type(o) {
    return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? 'object' : 'null');
};

Dadurch werden viele der von Javascript bereitgestellten Grundelemente erfasst. Sie können jedoch jederzeit weitere hinzufügen, indem Sie das TYPESObjekt ändern . Beachten Sie, dass typeof HTMLElementCollectionin Safari ein Bericht erstellt functionwird, der Typ (HTMLElementCollection) jedoch zurückgegeben wirdobject

Nick Husher
quelle
31

Die folgende Funktion kann hilfreich sein:

function typeOf(obj) {
  return {}.toString.call(obj).split(' ')[1].slice(0, -1).toLowerCase();
}

Oder in ES7 (Kommentar bei weiteren Verbesserungen)

function typeOf(obj) {
  const { toString } = Object.prototype;
  const stringified = obj::toString();
  const type = stringified.split(' ')[1].slice(0, -1);

  return type.toLowerCase();
}

Ergebnisse:

typeOf(); //undefined
typeOf(null); //null
typeOf(NaN); //number
typeOf(5); //number
typeOf({}); //object
typeOf([]); //array
typeOf(''); //string
typeOf(function () {}); //function
typeOf(/a/) //regexp
typeOf(new Date()) //date
typeOf(new Error) //error
typeOf(Promise.resolve()) //promise
typeOf(function *() {}) //generatorfunction
typeOf(new WeakMap()) //weakmap
typeOf(new Map()) //map

Vielen Dank an @johnrees, dass Sie mich benachrichtigt haben über: Fehler, Versprechen, Generatorfunktion

Vix
quelle
Danke dafür. Es funktioniert auch für Datumsobjekte:typeOf(new Date())
James Irwin
Ah, das hatte ich vergessen - danke, es ist jetzt enthalten.
Vix
1
UndtypeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
Johnrees
Ich kann diese Antwort nicht in Typoskript arbeiten lassen. Gibt Fehler:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK
Ich bin mir bei TypeScript nicht sicher. Haben Sie die ES7-Version ausprobiert? toString, das nicht explizit deklariert wird, könnte die Ursache sein?
Vix
15

Wir können auch ein kleines Beispiel von ipr101 ändern

Object.prototype.toType = function() {
  return ({}).toString.call(this).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}

und anrufen als

"aaa".toType(); // 'string'
Greymaster
quelle
6
Denken Sie daran, dass das Manipulieren von JavaScript-Basisobjekten zu Kompatibilität und anderen seltsamen Problemen führen kann. Versuchen Sie, dies sowohl in Bibliotheken als auch in größeren Projekten sparsam einzusetzen!
Alexander Craggs
9

Einzeilenfunktion:

function type(obj) {
    return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/,"$1").toLowerCase()
}

Dies ergibt das gleiche Ergebnis wie jQuery.type()

Yukulélé
quelle
3

Sie können Object.prototype.toStringauf jedes Objekt anwenden :

var toString = Object.prototype.toString;

console.log(toString.call([]));
//-> [object Array]

console.log(toString.call(/reg/g));
//-> [object RegExp]

console.log(toString.call({}));
//-> [object Object]

Dies funktioniert in allen Browsern mit Ausnahme des IE gut. Wenn Sie dies für eine Variable aufrufen, die aus einem anderen Fenster stammt, wird es nur ausgespuckt [object Object].

Andy E.
quelle
1
@Xeon Ich denke, der Hass gegen IE ist ein bisschen zu viel. IE9 ist ein viel besserer Browser als seine Vorgänger.
Aillyn
4
@pessimopoppotamus, aber niemand, der seinen Browser auf IE9 aktualisieren würde, verwendet IE.
Alex Turpin
1
IE 9 ist ein Schritt in die richtige Richtung, IE 10 sieht ziemlich gut aus. Der Fehler, den ich erwähnte, war übrigens eine Regression in IE 9 - sie haben ihn in IE 8 behoben.
Andy E
3

Meine 2 ¢! Wirklich, ein Teil des Grundes, warum ich dies hier trotz der langen Liste von Antworten vorwerfe, besteht darin, eine etwas all in onetypischere Lösung bereitzustellen und in Zukunft Feedback zu erhalten, wie man sie erweitern kann, um mehr aufzunehmen real types.

Wie oben erwähnt, habe ich mit der folgenden Lösung einige der hier gefundenen Lösungen kombiniert und einen Fix für die Rückgabe eines Werts für ein von jQueryjQuery definiertes Objekt integriert, sofern verfügbar . Ich hänge die Methode auch an den nativen Objektprototyp an. Ich weiß, dass das oft tabu ist, da es andere solche Erweiterungen stören könnte, aber das überlasse ich user beware. Wenn Ihnen diese Vorgehensweise nicht gefällt, kopieren Sie einfach die Basisfunktion an eine beliebige Stelle und ersetzen Sie alle Variablen von thisdurch einen zu übergebenden Argumentparameter (z. B. Argumente [0]).

;(function() {  //  Object.realType
    function realType(toLower) {
        var r = typeof this;
        try {
            if (window.hasOwnProperty('jQuery') && this.constructor && this.constructor == jQuery) r = 'jQuery';
            else r = this.constructor && this.constructor.name ? this.constructor.name : Object.prototype.toString.call(this).slice(8, -1);
        }
        catch(e) { if (this['toString']) r = this.toString().slice(8, -1); }
        return !toLower ? r : r.toLowerCase();
    }
    Object['defineProperty'] && !Object.prototype.hasOwnProperty('realType')
        ? Object.defineProperty(Object.prototype, 'realType', { value: realType }) : Object.prototype['realType'] = realType;
})();

Dann einfach mit Leichtigkeit verwenden, so:

obj.realType()  //  would return 'Object'
obj.realType(true)  //  would return 'object'

Hinweis: Es ist 1 Argument passabel. Wenn bool of ist true, wird die Rückgabe immer in Kleinbuchstaben angegeben .

Mehr Beispiele:

true.realType();                            //  "Boolean"
var a = 4; a.realType();                    //  "Number"
$('div:first').realType();                   // "jQuery"
document.createElement('div').realType()    //  "HTMLDivElement"

Wenn Sie etwas hinzufügen möchten, das möglicherweise hilfreich ist, z. B. das Definieren, wann ein Objekt mit einer anderen Bibliothek erstellt wurde (Moo, Proto, Yui, Dojo usw.), können Sie dies gerne kommentieren oder bearbeiten, damit es weitergeht genau und präzise. ODER rollen Sie zu dem, den GitHubich dafür gemacht habe, und lassen Sie es mich wissen. Dort finden Sie auch einen schnellen Link zu einer cdn min-Datei.

SpYk3HH
quelle
2
function getType(obj) {
    if(obj && obj.constructor && obj.constructor.name) {
        return obj.constructor.name;
    }
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

In meinen Vorversuchen funktioniert das ziemlich gut. Der erste Fall gibt den Namen eines Objekts aus, das mit "neu" erstellt wurde, und der zweite Fall sollte alles andere erfassen.

Ich verwende, (8, -1)weil ich davon ausgehe, dass das Ergebnis immer mit beginnen [objectund enden wird, ]aber ich bin nicht sicher, ob dies in jedem Szenario zutrifft.

mpen
quelle
2

Ich denke, die universellste Lösung hier ist - zuerst zu suchen undefinedund nulldann einfach anzurufen constructor.name.toLowerCase().

const getType = v =>
  v === undefined
    ? 'undefined'
    : v === null
      ? 'null'
      : v.constructor.name.toLowerCase();




console.log(getType(undefined)); // 'undefined'
console.log(getType(null)); // 'null'
console.log(getType('')); // 'string'
console.log(getType([])); // 'array'
console.log(getType({})); // 'object'
console.log(getType(new Set())); // `set'
console.log(getType(Promise.resolve())); // `promise'
console.log(getType(new Map())); // `map'
Arsenowitch
quelle
Soweit ich weiß - .constructor.nameenthält der immer den Stringwert. Für undefined / null haben wir die entsprechenden Zeichenfolgen, die von der Funktion zurückgegeben werden.
Arsenowitch
Danke - meine Kommentare löschen ...
Bergi
Da constructores sich um eine geerbte Eigenschaft handelt, wird dies für Objekte unterbrochen, die mit erstellt wurden Object.create(null). Einfach genug zu reparieren, aber wie soll man es nennen? "[Objekt null]"?
traktor53
0

typeof Bedingung wird verwendet, um den Variablentyp zu überprüfen, wenn Sie den Variablentyp in der if-else-Bedingung überprüfen, z

if(typeof Varaible_Name "undefined")
{

}
MzkZeeshan
quelle