¿Por qué instancia de devuelve falso para algunos literales?

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

Los literales de matriz y los literales de objeto coinciden ...

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

¿Por qué no todos ellos? O, ¿por qué no todos ellos no ?
¿Y de qué son una instancia, entonces?

Es lo mismo en FF3, IE7, Opera y Chrome. Entonces, al menos es consistente.


Perdí algunas.

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

Respuestas:

424

Las primitivas son un tipo diferente de tipo que los objetos creados desde Javascript. De los documentos de la API de 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)

No puedo encontrar ninguna manera de construir tipos primitivos con código, tal vez no sea posible. Esta es probablemente la razón por la cual la gente usa en typeof "foo" === "string"lugar de instanceof.

Una manera fácil de recordar cosas como esta es preguntarse: "Me pregunto qué sería sensato y fácil de aprender". Cualquiera que sea la respuesta, Javascript hace lo otro.

John Millikin
fuente
55
Todos los días con una nueva razón para odiar JavaScript es un buen día. Sé que hace mucho tiempo, pero gracias por esta publicación.
toniedzwiedz
57
Tu terminología está mal. La palabra "literal" se refiere a una sintaxis para crear datos sin usar un constructor. No se refiere a los datos resultantes. La sintaxis literal se puede usar para crear objetos y no objetos. El término correcto es "primitivas", que se refieren a datos no objeto. Algunos datos tienen representaciones primitivas y de objeto. La cadena es uno de esos tipos de datos.
estado gris llegará el
14
Para su información, puede crear primitivas sin sintaxis literal. (new String()).valueOf();
estado gris llegará el
11
Tenga en cuenta que typeof foo === 'string'no es suficiente: vea la respuesta de axkibe.
Bryan Larsen
1
Además, typeof new String('')regresa"object"
transang
105

Yo suelo:

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

Porque en JavaScript las cadenas pueden ser literales u objetos.

axkibe
fuente
28
Encontré algo corto por cierto. function isString(s) { return s.constructor === String; }Funciona para literales y objetos de cadena (al menos en V8)
axkibe
77
Tengo que amar a JavaScript.
Derek 朕 會 功夫
2
Uso jQuery.type (s) === 'string' ( api.jquery.com/jquery.type ), jQuery.isArray (), jQuery.isFunction (), jQuery.isNumeric () cuando es posible.
Ivan Samygin
1
@axkibe mientras estás en lo correcto, no es tan eficiente como typeof.
Qix - MONICA FUE MAL
Puedes usar typeof "?" == String.name.toLowerCase () [pero ¿por qué es [] instanceof Array?]
QuentinUK
62

En JavaScript todo es un objeto (o al menos puede tratarse como un objeto), excepto las primitivas (booleanos, nulos, números, cadenas y el valor undefined(y símbolo en 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

Como puede ver, los objetos, las matrices y el valor nullse consideran objetos ( nulles una referencia a un objeto que no existe). Las funciones se distinguen porque son un tipo especial de objetos invocables . Sin embargo, siguen siendo objetos.

Por otro lado los literales true, 0, ""y undefinedno son objetos. Son valores primitivos en JavaScript. Sin embargo, los booleanos, los números y las cadenas también tienen constructores Boolean, Numbery Stringrespectivamente, que envuelven sus primitivas respectivas para proporcionar una funcionalidad adicional:

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

Como puede ver cuando los valores primitivos se envuelven dentro de Boolean, Numbery los Stringconstructores, respectivamente, se convierten en objetos. El instanceofoperador solo funciona para objetos (por lo que devuelve falsevalores primitivos):

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

Como puede ver ambos typeofy instanceofno son suficientes para probar si un valor es un valor booleano, un número o una cadena, typeofsolo funciona para booleanos, números y cadenas primitivos; y instanceofno funciona para booleanos, números y cadenas primitivos.

Afortunadamente, hay una solución simple para este problema. La implementación predeterminada de toString(es decir, como se define de forma nativa Object.prototype.toString) devuelve la [[Class]]propiedad interna de los valores y objetos primitivos:

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]]propiedad interna de un valor es mucho más útil que typeofel valor. Podemos usar Object.prototype.toStringpara crear nuestra propia versión (más útil) del typeofoperador de la siguiente manera:

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

Espero que este artículo haya ayudado. Para saber más sobre las diferencias entre los primitivos y los objetos envueltos, lea la siguiente publicación de blog: La vida secreta de los primitivos de JavaScript

Aadit M Shah
fuente
66
+1, aunque también nulles un valor primitivo (solo el typeofoperador es confuso)
Bergi
33

Puede usar la propiedad constructor:

'foo'.constructor == String // returns true
true.constructor == Boolean // returns true
usuario144049
fuente
18
Tenga en cuenta que al probar variables, esta técnica puede fallar en ciertas circunstancias. Hay una referencia implícita a la ventana actual delante Stringy Booleanen el ejemplo anterior, por lo que si está probando la constructorpropiedad de una variable de cadena creada en otra ventana (como una ventana emergente o un marco), no será igual a simplemente String, será ser igual thatOtherWindowsName.String.
Michael Mathews
¿Y la instancia no trata con esto y devuelve el resultado booleano apropiado?
Chris Noe
55
esto falla si se le pasa un descendiente de String.
Bryan Larsen
1
@MichaelMathews: Esto funciona para remediar eso:Object.prototype.toString.call('foo') === '[object String]'
rvighne
@BryanLarsen y @MichaelMathews ¿Hay algún problema al usarlo d.constructor == String? Por ejemplo, con un operador de igualdad suelto.
dotnetCarpenter
7
 typeof(text) === 'string' || text instanceof String; 

puede usar esto, funcionará tanto para el caso como

  1. var text="foo"; // typeof funcionará

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

saurabhgoyal795
fuente
3

Esto se define en la sección 7.3.19 de la especificación ECMAScript Paso 3 :If Type(O) is not Object, return false.

En otras palabras, si el Objen Obj instanceof Callableno es un objeto, la instanceofvoluntad de corto circuito para falsedirectamente.

HKTonyLee
fuente
1

Creo que se me ocurrió una solución viable:

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

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

Devuelve una representación de cadena de instanceof(el nombre del constructor)

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
fuente
-2

Para mí la confusión causada por

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

Por "str" istanceof Stringlo tanto, debe regresar trueporque cómo istanceof funciona como a continuación:

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

Los resultados de las expresiones n . ° 1 y n . ° 2 entran en conflicto, por lo que debería haber uno de ellos incorrecto.

# 1 está mal

Me doy cuenta de que es causado por la __proto__propiedad no estándar, así que use la estándar:Object.getPrototypeOf

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

Ahora no hay confusión entre la expresión # 2 y # 3

mko
fuente
2
# 1 es correcto, pero se debe al descriptor de acceso de propiedad , que encajona el valor primitivo a su tipo de objeto respectivo, similar a Object("str").__proto__o Object("str") instanceof String.
Jonathan Lonowski el
@ JonathanLonowski gracias por señalar esto. No sabía eso
mko
-8

O simplemente puede hacer su propia función de esta manera:

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

uso:

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

Ambos deberían volver verdadero.

algo
fuente
14
Ya veo eval. Mal.
Aaria Carter-Weir