Warum gibt instanceof für einige Literale false zurück?

284
"foo" instanceof String //=> false
"foo" instanceof Object //=> false
true instanceof Boolean //=> false
true instanceof Object //=> false
false instanceof Boolean //=> false
false instanceof Object //=> false

// the tests against Object really don't make sense

Array-Literale und Objekt-Literale stimmen überein ...

[0,1] instanceof Array //=> true
{0:1} instanceof Object //=> true

Warum nicht alle? Oder warum nicht alle nicht ?
Und wofür sind sie dann ein Beispiel?

Dies gilt auch für FF3, IE7, Opera und Chrome. Zumindest ist es also konsistent.


Habe ein paar verpasst.

12.21 instanceof Number //=> false
/foo/ instanceof RegExp //=> true
Jonathan Lonowski
quelle

Antworten:

424

Grundelemente sind eine andere Art von Typ als Objekte, die in Javascript erstellt wurden. Aus den Mozilla API-Dokumenten :

var color1 = new String("green");
color1 instanceof String; // returns true
var color2 = "coral";
color2 instanceof String; // returns false (color2 is not a String object)

Ich kann keine Möglichkeit finden, primitive Typen mit Code zu konstruieren, vielleicht ist das nicht möglich. Dies ist wahrscheinlich der Grund, warum Menschen typeof "foo" === "string"anstelle von verwenden instanceof.

Eine einfache Möglichkeit, sich an solche Dinge zu erinnern, besteht darin, sich zu fragen: "Ich frage mich, was vernünftig und leicht zu lernen wäre." Was auch immer die Antwort ist, Javascript macht das andere.

John Millikin
quelle
5
Jeder Tag mit einem neuen Grund, JavaScript zu hassen, ist ein guter Tag. Ich weiß, dass es längst überfällig ist, aber ich danke Ihnen für diesen Beitrag.
toniedzwiedz
57
Ihre Terminologie ist falsch. Das Wort "Literal" bezieht sich auf eine Syntax zum Erstellen von Daten ohne Verwendung eines Konstruktors. Es bezieht sich nicht auf die resultierenden Daten. Die Literal-Syntax kann verwendet werden, um sowohl Objekte als auch Nicht-Objekte zu erstellen. Der richtige Begriff ist "Grundelemente", die sich auf Nicht-Objektdaten beziehen. Einige Daten haben sowohl primitive als auch Objektdarstellungen. String ist einer dieser Datentypen.
grauer Zustand kommt
14
Zu Ihrer Information, Sie können Grundelemente ohne Literal-Syntax erstellen. (new String()).valueOf();
grauer Zustand kommt
11
Beachten Sie, dass dies typeof foo === 'string'nicht ausreicht: Siehe die Antwort von axkibe.
Bryan Larsen
1
Darüber hinaus typeof new String('')kehrt"object"
transang
105

Ich benutze:

function isString(s) {
    return typeof(s) === 'string' || s instanceof String;
}

Denn in JavaScript können Zeichenfolgen Literale oder Objekte sein.

Axkibe
quelle
28
Ich habe übrigens etwas Kurzes gefunden. function isString(s) { return s.constructor === String; }Funktioniert für Literale und String-Objekte (zumindest in V8)
Axkibe
7
Ich muss JavaScript lieben.
Derek 27 會 功夫
2
Ich verwende jQuery.type (s) === 'string' ( api.jquery.com/jquery.type ), jQuery.isArray (), jQuery.isFunction (), jQuery.isNumeric (), wenn es möglich ist.
Ivan Samygin
1
@axkibe, während Sie richtig sind, ist es bei weitem nicht so performant wie typeof.
Qix - MONICA wurde am
Sie können typeof "?" == String.name.toLowerCase () [aber warum ist [] Instanz von Array?]
QuentinUK
62

In JavaScript ist alles ein Objekt (oder zumindest als ein Objekt behandelt werden), mit Ausnahme Primitiven (booleans, null, Zahlen, Strings und dem Wert undefined(und das Symbol in ES6)):

console.log(typeof true);           // boolean
console.log(typeof 0);              // number
console.log(typeof "");             // string
console.log(typeof undefined);      // undefined
console.log(typeof null);           // object
console.log(typeof []);             // object
console.log(typeof {});             // object
console.log(typeof function () {}); // function

Wie Sie sehen können, werden Arrays und der Wert nullalle als Objekte betrachtet ( nullist ein Verweis auf ein Objekt, das nicht existiert). Funktionen werden unterschieden, weil sie eine spezielle Art von aufrufbaren Objekten sind. Sie sind jedoch immer noch Objekte.

Auf der anderen Seite der Literale true, 0, ""und undefinedsind nicht Objekte. Sie sind primitive Werte in JavaScript. Allerdings booleans, Zahlen und Strings auch Konstrukteure haben Boolean, Numberund Stringjeweils die ihre jeweiligen Primitiven wickeln zusätzliche Funktionen bereitzustellen:

console.log(typeof new Boolean(true)); // object
console.log(typeof new Number(0));     // object
console.log(typeof new String(""));    // object

Wie Sie sehen können , wenn primitive Werte innerhalb der umhüllte sind Boolean, Numberund StringKonstrukteuren bzw. werden sie Objekte. Der instanceofOperator arbeitet nur für Objekte (weshalb er falsefür primitive Werte zurückgibt ):

console.log(true instanceof Boolean);              // false
console.log(0 instanceof Number);                  // false
console.log("" instanceof String);                 // false
console.log(new Boolean(true) instanceof Boolean); // true
console.log(new Number(0) instanceof Number);      // true
console.log(new String("") instanceof String);     // true

Wie Sie beide sehen können typeofund instanceofnicht ausreichen, um zu testen, ob ein Wert ein Boolescher Wert, eine Zahl oder eine Zeichenfolge ist, typeoffunktioniert dies nur für primitive Boolesche Werte, Zahlen und Zeichenfolgen. und instanceoffunktioniert nicht für primitive Boolesche Werte, Zahlen und Zeichenfolgen.

Glücklicherweise gibt es eine einfache Lösung für dieses Problem. Die Standardimplementierung von toString(dh wie sie nativ definiert ist Object.prototype.toString) gibt die interne [[Class]]Eigenschaft von primitiven Werten und Objekten zurück:

function classOf(value) {
    return Object.prototype.toString.call(value);
}

console.log(classOf(true));              // [object Boolean]
console.log(classOf(0));                 // [object Number]
console.log(classOf(""));                // [object String]
console.log(classOf(new Boolean(true))); // [object Boolean]
console.log(classOf(new Number(0)));     // [object Number]
console.log(classOf(new String("")));    // [object String]

Die interne [[Class]]Eigenschaft eines Werts ist viel nützlicher als typeofder Wert. Wir können verwenden Object.prototype.toString, um unsere eigene (nützlichere) Version des typeofOperators wie folgt zu erstellen :

function typeOf(value) {
    return Object.prototype.toString.call(value).slice(8, -1);
}

console.log(typeOf(true));              // Boolean
console.log(typeOf(0));                 // Number
console.log(typeOf(""));                // String
console.log(typeOf(new Boolean(true))); // Boolean
console.log(typeOf(new Number(0)));     // Number
console.log(typeOf(new String("")));    // String

Hoffe dieser Artikel hat geholfen. Um mehr über die Unterschiede zwischen Grundelementen und umschlossenen Objekten zu erfahren, lesen Sie den folgenden Blog-Beitrag: Das geheime Leben von JavaScript-Grundelementen

Aadit M Shah
quelle
6
+1, obwohl nullist auch ein primitiver Wert (nur der typeofOperator ist verwirrend)
Bergi
33

Sie können die Konstruktoreigenschaft verwenden:

'foo'.constructor == String // returns true
true.constructor == Boolean // returns true
user144049
quelle
18
Beachten Sie, dass diese Technik beim Testen von Variablen unter bestimmten Umständen fehlschlagen kann. Es eine implizite Referenz auf das aktuelle Fenster vor Stringund Booleanin dem obigen Beispiel, wenn Sie also die Prüfung sind constructorEigenschaft eines String - Variable in einem anderen Fenster erstellt (wie ein Popup oder Rahmen) wird es nicht einfach gleich sein String, wird es gleich sein thatOtherWindowsName.String.
Michael Mathews
Und geht Instanz davon nicht damit um und gibt das entsprechende boolesche Ergebnis zurück?
Chris Noe
5
Dies schlägt fehl, wenn Sie einen Nachkommen von String übergeben haben.
Bryan Larsen
1
@ MichaelMathews: Dies funktioniert, um das zu beheben:Object.prototype.toString.call('foo') === '[object String]'
Rvighne
@BryanLarsen und @MichaelMathews Gibt es Probleme bei der Verwendung d.constructor == String? ZB mit einem losen Gleichheitsoperator.
dotnetCarpenter
7
 typeof(text) === 'string' || text instanceof String; 

Sie können dies verwenden, es wird für beide Fälle als funktionieren

  1. var text="foo"; // typeof wird funktionieren

  2. String text= new String("foo"); // instanceof wird funktionieren

saurabhgoyal795
quelle
3

Dies ist in der ECMAScript-Spezifikation in Abschnitt 7.3.19 definiert. Schritt 3 :If Type(O) is not Object, return false.

Mit anderen Worten, wenn das ObjIn Obj instanceof Callablekein Objekt ist, instanceofwird das falsedirekt kurzgeschlossen .

HKTonyLee
quelle
1

Ich glaube, ich habe eine tragfähige Lösung gefunden:

Object.getPrototypeOf('test') === String.prototype    //true
Object.getPrototypeOf(1) === String.prototype         //false
Robby Harris
quelle
-1

https://www.npmjs.com/package/typeof

Gibt eine Zeichenfolgendarstellung von instanceof(dem Konstruktornamen) zurück.

function instanceOf(object) {
  var type = typeof object

  if (type === 'undefined') {
    return 'undefined'
  }

  if (object) {
    type = object.constructor.name
  } else if (type === 'object') {
    type = Object.prototype.toString.call(object).slice(8, -1)
  }

  return type.toLowerCase()
}

instanceOf(false)                  // "boolean"
instanceOf(new Promise(() => {}))  // "promise"
instanceOf(null)                   // "null"
instanceOf(undefined)              // "undefined"
instanceOf(1)                      // "number"
instanceOf(() => {})               // "function"
instanceOf([])                     // "array"
samdd
quelle
-2

Für mich die Verwirrung verursacht durch

"str".__proto__ // #1
=> String

Also "str" istanceof Stringsollte zurückkehren, truedenn wie istanceof wie folgt funktioniert:

"str".__proto__ == String.prototype // #2
=> true

Die Ergebnisse von Ausdruck Nr. 1 und Nr. 2 stehen in Konflikt miteinander, daher sollte einer von ihnen falsch sein.

# 1 ist falsch

Ich finde heraus, dass es durch die __proto__nicht standardmäßige Eigenschaft verursacht wird. Verwenden Sie also die Standardeigenschaft:Object.getPrototypeOf

Object.getPrototypeOf("str") // #3
=> TypeError: Object.getPrototypeOf called on non-object

Jetzt gibt es keine Verwechslung zwischen Ausdruck 2 und 3

mko
quelle
2
# 1 ist korrekt, aber es liegt am Eigenschafts-Accessor , der den primitiven Wert in seinen jeweiligen Objekttyp einfügt, ähnlich wie Object("str").__proto__oder Object("str") instanceof String.
Jonathan Lonowski
@ JonathanLonowski danke für den Hinweis. Ich wusste das nicht
mko
-8

Oder Sie können einfach Ihre eigene Funktion wie folgt erstellen:

function isInstanceOf(obj, clazz){
  return (obj instanceof eval("("+clazz+")")) || (typeof obj == clazz.toLowerCase());
};

Verwendung:

isInstanceOf('','String');
isInstanceOf(new String(), 'String');

Diese sollten beide true zurückgeben.

etw
quelle
14
Ich sehe eval. Böse.
Aaria Carter-Weir