Por que instanceof retorna false para alguns literais?

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

Literais de matriz e literais de objeto correspondem ...

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

Por que não todos eles? Ou, por que não todos eles não ?
E, de que são uma instância, então?

É o mesmo no FF3, IE7, Opera e Chrome. Então, pelo menos é consistente.


Perdeu alguns.

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

Respostas:

424

As primitivas são um tipo de tipo diferente dos objetos criados no Javascript. Nos documentos da API da 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)

Não consigo encontrar nenhuma maneira de construir tipos primitivos com código, talvez não seja possível. Provavelmente é por isso que as pessoas usam em typeof "foo" === "string"vez de instanceof.

Uma maneira fácil de lembrar coisas assim é se perguntar: "Será que seria sensato e fácil de aprender"? Seja qual for a resposta, o Javascript faz a outra coisa.

John Millikin
fonte
5
Todos os dias com um novo motivo para odiar o JavaScript é um bom dia. Sei que está muito atrasado, mas agradeço por este post.
toniedzwiedz 03/09/12
57
Sua terminologia está errada. A palavra "literal" refere-se a uma sintaxe para criar dados sem usar um construtor. Não se refere aos dados resultantes. Sintaxe literal pode ser usada para criar objetos e não-objetos. O termo correto é "primitivo", que se refere a dados que não são objetos. Alguns dados têm representações primitivas e de objeto. String é um desses tipos de dados.
gray state está chegando em
14
Para sua informação, você pode criar primitivas sem sintaxe literal. (new String()).valueOf();
gray state está chegando em
11
Note que isso typeof foo === 'string'não é suficiente: veja a resposta do axkibe.
Bryan Larsen
1
Além disso, typeof new String('')retornos"object"
transang
105

Eu uso:

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

Como no JavaScript, as strings podem ser literais ou objetos.

axkibe
fonte
28
Eu encontrei algo shorte btw. function isString(s) { return s.constructor === String; }Trabalha para literais e objetos string (pelo menos em V8)
axkibe
7
Tenho que amar o JavaScript.
Derek
2
Eu uso jQuery.type (s) === 'string' ( api.jquery.com/jquery.type ), jQuery.isArray (), jQuery.isFunction (), jQuery.isNumeric () quando é possível.
precisa saber é o seguinte
1
@axkibe enquanto estiver correto, não é tão eficiente quanto typeof.
Qix - MONICA FOI ERRADA EM
Você pode usar typeof "?" == String.name.toLowerCase () [mas por que [] instanceof Array?]]
QuentinUK
62

Em JavaScript, tudo é um objeto (ou pelo menos pode ser tratado como um objeto), exceto primitivas (booleanos, nulos, números, seqüências de caracteres e o valor undefined(e símbolo no 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 você pode ver objetos, matrizes e o valor nullsão todos considerados objetos ( nullé uma referência a um objeto que não existe). As funções são diferenciadas porque são um tipo especial de objetos que podem ser chamados . No entanto, eles ainda são objetos.

Por outro lado, os literais true, 0, ""e undefinednão são objetos. Eles são valores primitivos em JavaScript. No entanto, booleanos, números e seqüências de caracteres também têm construtores e Boolean, respectivamente, que agrupam suas respectivas primitivas para fornecer funcionalidade adicional:NumberString

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

Como você pode ver quando os valores primitivos são agrupados nos construtores Boolean, Numbere Stringrespectivamente eles se tornam objetos. O instanceofoperador trabalha apenas para objetos (é por isso que retorna falsepara valores 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 você pode ver os dois typeofe instanceofé insuficiente para testar se um valor é um booleano, um número ou uma string - typeoffunciona apenas para booleanos primitivos, números e strings; e instanceofnão funciona para booleanos primitivos, números e seqüências de caracteres.

Felizmente, existe uma solução simples para esse problema. A implementação padrão de toString(ou seja, como é definida originalmente Object.prototype.toString) retorna a [[Class]]propriedade interna dos valores e 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]

A [[Class]]propriedade interna de um valor é muito mais útil que typeofo valor. Podemos usar Object.prototype.toStringpara criar nossa própria versão (mais útil) do typeofoperador da seguinte maneira:

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 artigo tenha ajudado. Para saber mais sobre as diferenças entre objetos primitivos e agrupados, leia a seguinte postagem no blog: A Vida Secreta das Primitivas JavaScript

Aadit M Shah
fonte
6
1, apesar nullé um valor primitivo , bem como (apenas o typeofoperador é confuso)
Bergi
33

Você pode usar a propriedade construtor:

'foo'.constructor == String // returns true
true.constructor == Boolean // returns true
user144049
fonte
18
Observe que, ao testar variáveis, essa técnica pode falhar em determinadas circunstâncias. Há uma referência implícita à janela atual na frente Stringe Booleanno exemplo acima, portanto, se você estiver testando a constructorpropriedade de uma variável de string criada em outra janela (como um pop-up ou quadro), ela não será igual a simplesmente String, será seja igual a thatOtherWindowsName.String.
Michael Mathews
E a instância não lida com isso e retorna o resultado booleano apropriado?
Chris #
5
isso falhará se você tiver passado um descendente de String.
Bryan Larsen
1
@MichaelMathews: Isso funciona para remédio que:Object.prototype.toString.call('foo') === '[object String]'
rvighne
@BryanLarsen e @MichaelMathews Há algum problema no uso d.constructor == String? Por exemplo, com um operador de igualdade frouxo.
DotnetCarpenter
7
 typeof(text) === 'string' || text instanceof String; 

você pode usar isso, ele funcionará nos dois casos

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

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

saurabhgoyal795
fonte
3

Isso é definido na seção 7.3.19 da especificação ECMAScript, etapa 3 :If Type(O) is not Object, return false.

Em outras palavras, se o Objin Obj instanceof Callablenão for um objeto, o instanceofcurto-circuito será falsediretamente.

HKTonyLee
fonte
1

Acredito ter encontrado uma solução viável:

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

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

Retorna uma representação de string de instanceof(o nome dos construtores)

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

Para mim, a confusão causada por

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

Então, "str" istanceof Stringdeve retornar trueporque como istanceof funciona como abaixo:

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

Os resultados das expressões # 1 e # 2 conflitam entre si; portanto, deve haver um deles errado.

# 1 está errado

Descobri que ele é causado pela __proto__propriedade não padrão, então use o padrão:Object.getPrototypeOf

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

Agora não há confusão entre as expressões 2 e 3

mko
fonte
2
O número 1 está correto, mas é devido ao acessador de propriedade , que coloca o valor primitivo em seu respectivo tipo de objeto, semelhante a Object("str").__proto__ou Object("str") instanceof String.
Jonathan Lonowski
@ JonathanLonowski obrigado por apontar isso. Eu não sabia disso
mko
-8

Ou você pode simplesmente fazer sua própria função assim:

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

uso:

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

Ambos devem retornar verdadeiro.

sth
fonte
14
Eu vejo eval. Mal.
Aaria Carter-Weir