Pourquoi instanceof renvoie false pour certains littéraux?

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

Les littéraux de tableau et les littéraux d'objet correspondent ...

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

Pourquoi pas tous? Ou, pourquoi ne pas tous pas ?
Et de quoi sont-ils un exemple, alors?

C'est la même chose dans FF3, IE7, Opera et Chrome. Donc, au moins, c'est cohérent.


En a manqué quelques-uns.

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

Réponses:

424

Les primitives sont d'un type différent de celui des objets créés à partir de Javascript. À partir des documents de l'API Mozilla :

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

Je ne trouve aucun moyen de construire des types primitifs avec du code, ce n'est peut-être pas possible. C'est probablement pourquoi les gens utilisent typeof "foo" === "string"au lieu de instanceof.

Un moyen facile de se souvenir de choses comme celle-ci est de se demander "Je me demande ce qui serait sain et facile à apprendre"? Quelle que soit la réponse, Javascript fait l'autre chose.

John Millikin
la source
5
Chaque jour avec une nouvelle raison de détester JavaScript est une bonne journée. Je sais qu'il est attendu depuis longtemps mais je vous remercie pour ce post.
toniedzwiedz
57
Votre terminologie est fausse. Le mot «littéral» fait référence à une syntaxe pour créer des données sans utiliser de constructeur. Il ne fait pas référence aux données résultantes. La syntaxe littérale peut être utilisée pour créer des objets et des non-objets. Le terme correct est "primitives", qui se réfèrent à des données non-objet. Certaines données ont des représentations à la fois primitives et d'objets. La chaîne est l'un de ces types de données.
l'état gris arrive le
14
Pour info, vous pouvez créer des primitives sans syntaxe littérale. (new String()).valueOf();
l'état gris arrive le
11
Notez que cela typeof foo === 'string'ne suffit pas: voir la réponse d'Axkibe.
Bryan Larsen
1
En plus, typeof new String('')retours"object"
transang
105

J'utilise:

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

Parce qu'en JavaScript, les chaînes peuvent être des littéraux ou des objets.

axkibe
la source
28
J'ai trouvé quelque chose de plus court. function isString(s) { return s.constructor === String; }Fonctionne pour les littéraux et les objets chaîne (au moins en V8)
axkibe
7
Je dois aimer JavaScript.
Derek 朕 會 功夫
2
J'utilise jQuery.type (s) === 'string' ( api.jquery.com/jquery.type ), jQuery.isArray (), jQuery.isFunction (), jQuery.isNumeric () quand c'est possible.
Ivan Samygin
1
@axkibe tant que vous avez raison, ce n'est pas aussi performant que typeof.
Qix - MONICA A ÉTÉ BRISÉE le
Vous pouvez utiliser le type de "?" == String.name.toLowerCase () [mais pourquoi est [] instanceof Array?]
QuentinUK
62

En JavaScript, tout est un objet (ou peut au moins être traité comme un objet), à l'exception des primitives (booléens, null, nombres, chaînes et la valeur undefined(et le symbole dans 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

Comme vous pouvez le voir, les tableaux et la valeur nullsont tous considérés comme des objets ( nullc'est une référence à un objet qui n'existe pas). Les fonctions se distinguent parce qu'elles sont un type spécial d' objets appelables . Mais ce sont toujours des objets.

D'autre part , les littéraux true, 0, ""et undefinedne sont pas des objets. Ce sont des valeurs primitives en JavaScript. Cependant, les booléens, les nombres et les chaînes ont également des constructeurs Boolean, Numberet Stringrespectivement qui enveloppent leurs primitives respectives pour fournir des fonctionnalités supplémentaires:

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

Comme vous pouvez le voir lorsque les valeurs primitives sont enveloppées respectivement dans les constructeurs et Boolean, elles deviennent des objets. L' opérateur ne fonctionne que pour les objets (c'est pourquoi il retourne pour les valeurs primitives):NumberStringinstanceoffalse

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

Comme vous pouvez voir les deux typeofet instanceofsont insuffisants pour tester si une valeur est un booléen, un nombre ou une chaîne - typeofne fonctionne que pour les booléens primitifs, les nombres et les chaînes; et instanceofne fonctionne pas pour les booléens primitifs, les nombres et les chaînes.

Heureusement, il existe une solution simple à ce problème. L'implémentation par défaut de toString(c'est-à-dire telle qu'elle est définie de manière native sur Object.prototype.toString) renvoie la [[Class]]propriété interne des valeurs primitives et des objets:

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]

La [[Class]]propriété interne d'une valeur est beaucoup plus utile que typeofla valeur. Nous pouvons utiliser Object.prototype.toStringpour créer notre propre version (plus utile) de l' typeofopérateur comme suit:

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

J'espère que cet article vous a aidé. Pour en savoir plus sur les différences entre les primitives et les objets encapsulés, lisez le billet de blog suivant: La vie secrète des primitives JavaScript

Aadit M Shah
la source
6
+1, altough nullest également une valeur primitive (seul l' typeofopérateur est déroutant)
Bergi
33

Vous pouvez utiliser la propriété constructeur:

'foo'.constructor == String // returns true
true.constructor == Boolean // returns true
user144049
la source
18
Notez que lors du test de variables, cette technique peut échouer dans certaines circonstances. Il y a une référence implicite à la fenêtre actuelle devant Stringet Booleandans l'exemple ci-dessus, donc si vous testez la constructorpropriété d'une variable de chaîne créée dans une autre fenêtre (comme un popup ou un cadre), elle ne sera pas simplement égale String, elle être égal à thatOtherWindowsName.String.
Michael Mathews
Et instanceof ne traite-t-il pas cela et ne renvoie-t-il pas le résultat booléen approprié?
Chris Noe
5
cela échoue si vous passez un descendant de String.
Bryan Larsen
1
@MichaelMathews: Cela fonctionne pour remédier à cela:Object.prototype.toString.call('foo') === '[object String]'
rvighne
@BryanLarsen et @MichaelMathews Y a-t-il un problème avec l'utilisation d.constructor == String? Par exemple, avec un opérateur d'égalité lâche.
dotnetCarpenter
7
 typeof(text) === 'string' || text instanceof String; 

vous pouvez l'utiliser, cela fonctionnera dans les deux cas comme

  1. var text="foo"; // typeof fonctionnera

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

saurabhgoyal795
la source
3

Ceci est défini dans la spécification ECMAScript Section 7.3.19 Étape 3 :If Type(O) is not Object, return false.

En d'autres termes, si l' Objentrée Obj instanceof Callablen'est pas un objet, la instanceofvolonté court-circuitera falsedirectement.

HKTonyLee
la source
1

Je pense avoir trouvé une solution viable:

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

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

Renvoie une représentation sous forme de chaîne de instanceof(le nom du constructeur)

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
la source
-2

Pour moi, la confusion causée par

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

Donc "str" istanceof Stringdevrait revenir trueparce que comment fonctionne istanceof comme ci-dessous:

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

Les résultats des expressions # 1 et # 2 sont en conflit, donc l'un d'eux devrait être faux.

# 1 est faux

Je pense que cela est dû à la __proto__propriété non standard, alors utilisez celle standard:Object.getPrototypeOf

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

Maintenant, il n'y a plus de confusion entre l'expression # 2 et # 3

mko
la source
2
# 1 est correct, mais cela est dû à l' accesseur de propriété , qui encadre la valeur primitive à son type d'objet respectif, similaire à Object("str").__proto__ou Object("str") instanceof String.
Jonathan Lonowski
@JonathanLonowski merci de l'avoir signalé. Je ne le savais pas
mko
-8

Ou vous pouvez simplement créer votre propre fonction comme ceci:

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

usage:

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

Celles-ci devraient toutes les deux retourner vrai.

qch
la source
14
Je vois eval. Mal.
Aaria Carter-Weir,