Perché instanceof restituisce false per alcuni letterali?


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

Letterali array e letterali oggetto corrispondono ...

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

Perché non tutti? Oppure, perché non lo fanno tutti no ?
E allora di cosa sono un'istanza?

È lo stesso in FF3, IE7, Opera e Chrome. Quindi, almeno è coerente.


Ne mancarono alcuni.

12.21 instanceof Number //=> false
/foo/ instanceof RegExp //=> true

Risposte:


424

I primitivi sono di tipo diverso rispetto agli oggetti creati da Javascript. Dai documenti API di 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)

Non riesco a trovare alcun modo per costruire tipi primitivi con il codice, forse non è possibile. Questo è probabilmente il motivo per cui le persone usano typeof "foo" === "string"invece di instanceof.

Un modo semplice per ricordare cose come questa è chiedersi "Mi chiedo cosa sarebbe sano e facile da imparare"? Qualunque sia la risposta, Javascript fa l'altra cosa.


5
Ogni giorno con un nuovo motivo per odiare JavaScript è una buona giornata. So che è atteso da tempo ma ti ringrazio per questo post.
toniedzwiedz,

57
La tua terminologia è sbagliata. La parola "letterale" si riferisce a una sintassi per la creazione di dati senza usare un costruttore. Non si riferisce ai dati risultanti. La sintassi letterale può essere utilizzata per creare sia oggetti che non oggetti. Il termine corretto è "primitive", che si riferiscono a dati non oggetto. Alcuni dati hanno rappresentazioni sia primitive che di oggetti. La stringa è uno di quei tipi di dati.
lo stato grigio arriverà il

14
Cordiali saluti, è possibile creare primitive senza sintassi letterale. (new String()).valueOf();
lo stato grigio arriverà il

11
Nota che typeof foo === 'string'non è abbastanza: vedi la risposta di axkibe.
Bryan Larsen,

1
Inoltre, typeof new String('')ritorna"object"
transang

105

Io uso:

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

Perché in JavaScript le stringhe possono essere letterali o oggetti.


28
Ho trovato qualcosa di simile a me. function isString(s) { return s.constructor === String; }Funziona per letterali e oggetti stringa (almeno in V8)
axkibe il

7
Devi amare JavaScript.
Derek 27 會 功夫 il

2
Uso jQuery.type (s) === 'string' ( api.jquery.com/jquery.type ), jQuery.isArray (), jQuery.isFunction (), jQuery.isNumeric () quando è possibile.
Ivan Samygin,

1
@axkibe mentre hai ragione, non è altrettanto performante come typeof.
Qix - MONICA È STATA MISTREATA il

Puoi usare typeof "?" == String.name.toLowerCase () [ma perché [] instanceof Array?]
QuentinUK

62

In JavaScript tutto è un oggetto (o almeno può essere trattato come un oggetto), tranne le primitive (booleane, null, numeri, stringhe e il valore undefined(e il simbolo 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

Come puoi vedere gli oggetti, le matrici e il valore nullsono tutti considerati oggetti ( nullè un riferimento a un oggetto che non esiste). Le funzioni si distinguono perché sono un tipo speciale di oggetti richiamabili . Tuttavia sono ancora oggetti.

D'altra parte i letterali true, 0, ""e undefinednon sono oggetti. Sono valori primitivi in ​​JavaScript. Tuttavia, i booleani, i numeri e le stringhe hanno anche dei costruttori e Boolean, rispettivamente, che avvolgono le rispettive primitive per fornire funzionalità aggiuntive:NumberString

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

Come puoi vedere quando i valori primitivi sono racchiusi all'interno di Boolean, Numbere Stringrispettivamente i costruttori diventano oggetti. L' instanceofoperatore funziona solo per gli oggetti (motivo per cui restituisce falsevalori primitivi):

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

Come puoi vedere entrambi typeofe instanceofnon sono sufficienti per verificare se un valore è un valore booleano, un numero o una stringa - typeoffunziona solo per valori booleani, numeri e stringhe primitivi; e instanceofnon funziona con booleani, numeri e stringhe primitivi.

Fortunatamente esiste una soluzione semplice a questo problema. L'implementazione predefinita di toString(ovvero come è nativamente definita Object.prototype.toString) restituisce la [[Class]]proprietà interna sia dei valori primitivi che degli oggetti:

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]]proprietà interna di un valore è molto più utile typeofdel valore. Possiamo usare Object.prototype.toStringper creare la nostra versione (più utile) typeofdell'operatore come segue:

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

Spero che questo articolo abbia aiutato. Per saperne di più sulle differenze tra primitivi e oggetti avvolti leggi il seguente post sul blog: The Secret Life of JavaScript Primitives


6
+1, anche se nullè un valore primitivo (solo l' typeofoperatore è confuso)
Bergi

33

È possibile utilizzare la proprietà del costruttore:

'foo'.constructor == String // returns true
true.constructor == Boolean // returns true

18
Si noti che durante il test delle variabili questa tecnica può fallire in determinate circostanze. C'è un riferimento implicito alla finestra corrente davanti Stringe Booleannell'esempio sopra, quindi se stai testando la constructorproprietà di una variabile stringa creata in un'altra finestra (come un popup o frame) non sarà uguale a semplicemente String, lo farà essere uguale a thatOtherWindowsName.String.
Michael Mathews,

E instanceof non si occupa di questo e restituisce il risultato booleano appropriato?
Chris Noe,

5
questo non riesce se si passa a un discendente di String.
Bryan Larsen,

1
@MichaelMathews: funziona per rimediare a questo:Object.prototype.toString.call('foo') === '[object String]'
rvighne,

@BryanLarsen e @MichaelMathews C'è qualche problema nell'uso d.constructor == String? Ad esempio con un operatore di uguaglianza libera.
dotnetCarpenter,

7
 typeof(text) === 'string' || text instanceof String; 

puoi usarlo, funzionerà per entrambi i casi come

  1. var text="foo"; // typeof funzionerà

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


3

Questo è definito nelle specifiche ECMAScript Sezione 7.3.19 Passaggio 3 :If Type(O) is not Object, return false.

In altre parole, se Objin Obj instanceof Callablenon è un oggetto, la instanceofvolontà esegue il corto circuito falsedirettamente.


1

Credo di aver trovato una soluzione praticabile:

Object.getPrototypeOf('test') === String.prototype    //true
Object.getPrototypeOf(1) === String.prototype         //false

-1

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

Restituisce una rappresentazione in formato stringa di instanceof(il nome del costruttore)

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"

-2

Per me la confusione causata da

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

Quindi "str" istanceof Stringdovrebbe tornare trueperché come funziona istanceof come di seguito:

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

I risultati delle espressioni n. 1 e n. 2 sono in conflitto tra loro, quindi dovrebbe essercene uno sbagliato.

# 1 è sbagliato

Ho capito che è causato dalla __proto__proprietà non standard, quindi usa quello standard:Object.getPrototypeOf

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

Ora non c'è confusione tra espressione # 2 e # 3


2
Il numero 1 è corretto, ma è dovuto all'accessorio proprietà , che racchiude il valore di base nel rispettivo tipo di oggetto, simile a Object("str").__proto__o Object("str") instanceof String.
Jonathan Lonowski il

@JonathanLonowski grazie per averlo segnalato. Non lo sapevo
mko il

-8

Oppure puoi semplicemente fare la tua funzione in questo modo:

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

utilizzo:

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

Entrambi dovrebbero tornare veri.


14
Vedo eval. Il male.
Aaria Carter-Weir,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.