Qual é a instância do operador em JavaScript?

313

A instanceofpalavra-chave em JavaScript pode ser bastante confusa quando é encontrada pela primeira vez, pois as pessoas tendem a pensar que o JavaScript não é uma linguagem de programação orientada a objetos.

  • O que é isso?
  • Que problemas ele resolve?
  • Quando é apropriado e quando não?
Alon Gubkin
fonte
3
Embora as respostas abaixo sejam muito úteis, você não as usa muito (pelo menos eu não) em aplicativos de palavras reais. Alguém poderia criar uma propriedade object.type que contém uma string e a verifica.
Omar Al-Ithawi
4
JS não faz nenhum sentido: "foo" instanceof String=> falso, 1 instanceof Number=> falso, {} instanceof Object=> falso. Diga o quê?!
morbusg
12
@morbusg seu comentário é enganador. Primeiro "foo" instanceof String => falseestá correto, porque typeof "foo" == 'string'. new String("foo") instanceof String => true, porque typeof String == 'function'- você deve tratar a função como classe (definição de classe). A variável se torna instanceofalguma function(classe) quando você a atribui como var v = new AnythingWhatTypeofEqualsFunction(). O mesmo se aplica a 1. typeof 1 == 'number'- 'number' não é 'function' :) Em seguida - {} instanceof Objectestá TRUEno nó e nos navegadores modernos
fider 12/12/13
1
@ fider: Esse foi um comentário sobre as especificações do idioma, vindo de um rubiista.
morbusg
@morbusg - ({}) instanceof Objectretornará true. De fato, o código que você escreveu causará um erro.
DDM

Respostas:

279

instancia de

O operando do lado esquerdo (LHS) é o objeto real sendo testado no operando do lado direito (RHS), que é o construtor real de uma classe. A definição básica é:

Checks the current object and returns true if the object
is of the specified object type.

Aqui estão alguns bons exemplos e um exemplo extraído diretamente do site de desenvolvedores da Mozilla :

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

Uma coisa que vale a pena mencionar é instanceofavaliada como verdadeira se o objeto herdar do protótipo da classe:

var p = new Person("Jon");
p instanceof Person

Isso é p instanceof Personverdade, pois pherda de Person.prototype.

Por solicitação do OP

Adicionei um pequeno exemplo com algum código de exemplo e uma explicação.

Quando você declara uma variável, atribui a ela um tipo específico.

Por exemplo:

int i;
float f;
Customer c;

O acima mostrar-lhe algumas variáveis, ou seja i, f, e c. Os tipos são integer, floate um utilizador definido Customertipo de dados. Tipos como o acima podem ser para qualquer idioma, não apenas JavaScript. No entanto, com JavaScript, quando você declara uma variável, você não define explicitamente um tipo var x, x pode ser um número / string / um tipo de dados definido pelo usuário. Então, o que instanceoffaz é checar o objeto para ver se é do tipo especificado, de cima, pegar o Customerobjeto que poderíamos fazer:

var c = new Customer();
c instanceof Customer; //Returns true as c is just a customer
c instanceof String; //Returns false as c is not a string, it's a customer silly!

Acima vimos que cfoi declarada com o tipo Customer. Nós o atualizamos e verificamos se é do tipo Customerou não. Certamente, ele retorna verdadeiro. Ainda usando o Customerobjeto, verificamos se é um String. Não, definitivamente não é um Stringnovo Customerobjeto, não um Stringobjeto. Nesse caso, ele retorna false.

É realmente muito simples!

JonH
fonte
@ Alon - eu adicionei um exemplo para você. Veja acima, color1 é do tipo string, então quando você diz que color1 instanceof String;isso retornará true porque color1 é uma string.
Jonh
@ Alon - Verifica um objeto para ver que tipo de objeto é. Considere um objeto de pessoa / cliente. Então person p = new person()p agora é um tipo de pessoa e não um tipo de string.
Jonh
Em JavaScript, pode ser complicado entender que uma variável tem a flexibilidade de ter um tipo diferente ao longo de sua vida útil. Exemplo de código: jsfiddle.net/sikusikucom/znSPv
moey
@ Siku-Siku.Com - Não vejo nenhum truque: var zero = 0; alert(zero); zero = "0"; alert(zero)passamos de um primitivo intpara um primitivo stringsem problemas.
18712 JonH
Oh, eu quis dizer que era complicado / novo para entender (não fazer ) que a "flexibilidade" em JavaScript, esp. para alguns que estão acostumados a um idioma que requer, por exemplo, conversão explícita para alterar um tipo de variável. Como você apontou, é muito fácil alterar o tipo de, por exemplo, primitivo intpara primitivo string.
moey
95

Há uma faceta importante, por exemplo, que parece não estar coberta em nenhum dos comentários até agora: herança. Uma variável que está sendo avaliada pelo uso de instanceof pode retornar true para vários "tipos" devido à herança prototípica.

Por exemplo, vamos definir um tipo e um subtipo:

function Foo(){ //a Foo constructor
    //assign some props
    return this;
}

function SubFoo(){ //a SubFoo constructor
    Foo.call( this ); //inherit static props
    //assign some new props
    return this;
}

SubFoo.prototype = Object.create(Foo.prototype); // Inherit prototype
SubFoo.prototype.constructor = SubFoo;

Agora que temos algumas "classes", vamos criar algumas instâncias e descobrir do que são instâncias:

var 
    foo = new Foo()
,   subfoo = new SubFoo()
;

alert( 
    "Q: Is foo an instance of Foo? "
+   "A: " + ( foo instanceof Foo ) 
); // -> true

alert( 
    "Q: Is foo an instance of SubFoo? " 
+   "A: " + ( foo instanceof SubFoo ) 
); // -> false

alert( 
    "Q: Is subfoo an instance of Foo? "
+   "A: " + ( subfoo instanceof Foo ) 
); // -> true

alert( 
    "Q: Is subfoo an instance of SubFoo? "
+   "A: " + ( subfoo instanceof SubFoo ) 
); // -> true

alert( 
    "Q: Is subfoo an instance of Object? "
+   "A: " + ( subfoo instanceof Object ) 
); // -> true

Vê a última linha? Todas as chamadas "novas" para uma função retornam um objeto que herda de Object. Isso vale mesmo quando se usa uma abreviação de criação de objeto:

alert( 
    "Q: Is {} an instance of Object? "
+   "A: " + ( {} instanceof Object ) 
); // -> true

E as próprias definições de "classe"? De que são exemplos?

alert( 
    "Q: Is Foo an instance of Object? "
+   "A:" + ( Foo instanceof Object) 
); // -> true

alert( 
    "Q: Is Foo an instance of Function? "
+   "A:" + ( Foo instanceof Function) 
); // -> true

Eu sinto que entender que qualquer objeto pode ser uma instância de vários tipos é importante, já que você (incorretamente) assume que pode diferenciar entre, digamos, objeto e uma função usando instanceof. Como este último exemplo mostra claramente uma função é um objeto.

Isso também é importante se você estiver usando qualquer padrão de herança e quiser confirmar a descendência de um objeto por outros métodos que não a digitação de patos.

Espero que ajude alguém a explorar instanceof.

webnesto
fonte
Ainda mais impressionante é que, depois de herdar o protótipo com SubFoo.prototype = new Foo();você pode adicionar mais métodos para isso, e o subfoo instanceof Foocheque ainda vai passar, bem comosubfoo instanceof SubFoo
Cory Danielson
87

As outras respostas aqui estão corretas, mas elas não explicam como instanceofrealmente funciona, o que pode ser do interesse de alguns advogados de idiomas por aí.

Todo objeto em JavaScript possui um protótipo, acessível através da __proto__propriedade As funções também têm uma prototypepropriedade, que é a inicial __proto__de qualquer objeto criado por elas. Quando uma função é criada, recebe um objeto exclusivo para prototype. O instanceofoperador usa essa exclusividade para dar uma resposta. Aqui está o que instanceofpode parecer se você o escreveu como uma função.

function instance_of(V, F) {
  var O = F.prototype;
  V = V.__proto__;
  while (true) {
    if (V === null)
      return false;
    if (O === V)
      return true;
    V = V.__proto__;
  }
}

Isso é basicamente parafraseando a ECMA-262 edição 5.1 (também conhecida como ES5), seção 15.3.5.3.

Observe que você pode reatribuir qualquer objeto à prototypepropriedade de uma função e pode reatribuir a __proto__propriedade de um objeto após sua construção. Isso fornecerá alguns resultados interessantes:

function F() { }
function G() { }
var p = {};
F.prototype = p;
G.prototype = p;
var f = new F();
var g = new G();

f instanceof F;   // returns true
f instanceof G;   // returns true
g instanceof F;   // returns true
g instanceof G;   // returns true

F.prototype = {};
f instanceof F;   // returns false
g.__proto__ = {};
g instanceof G;   // returns false
Jay Conrod
fonte
5
Vale ressaltar que a manipulação direta da __proto__propriedade " " não é permitida no IE. Se bem me lembro, a manipulação direta da propriedade também não está incluída nas especificações da ECMA; portanto, provavelmente é uma má idéia usá-la para tarefas que não sejam atividades acadêmicas.
webnesto 20/06/12
@webnesto, isso é verdade, proto não está na especificação. Eu não sabia que o IE não suportava isso. Quando você diz manipulação direta, você quer dizer que não está exposta ao código JS ou simplesmente não é gravável?
Jay Conrod
2
Não tenho 100% de certeza nas versões mais antigas. Parece daqui ( stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10 ) que no IE9 é pelo menos legível (embora não seja mutável). Também digno de nota que parece que os navegadores podem estar descartando completamente.
webnesto 6/07/12
4
É importante entender a existência implícita da propriedade proto, seja ela acessível ao código do usuário ou não. +10 se eu pudesse por citar as especificações, é exatamente isso que eu vim aqui procurando.
Mike Edwards
3
Para obter o link do protótipo, use Object.getPrototypeOf(o), será o mesmo que __proto__você descreve, mas está em conformidade com o ECMAScript.
Froginvasion
45

Eu acho que vale a pena notar que instanceof é definido pelo uso da palavra-chave "new" ao declarar o objeto. No exemplo de JonH;

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

O que ele não mencionou é isso;

var color1 = String("green");
color1 instanceof String; // returns false

A especificação de "new" na verdade copiou o estado final da função do construtor String para a var color1, em vez de apenas defini-lo com o valor de retorno. Eu acho que isso mostra melhor o que a nova palavra-chave faz;

function Test(name){
    this.test = function(){
        return 'This will only work through the "new" keyword.';
    }
    return name;
}

var test = new Test('test');
test.test(); // returns 'This will only work through the "new" keyword.'
test // returns the instance object of the Test() function.

var test = Test('test');
test.test(); // throws TypeError: Object #<Test> has no method 'test'
test // returns 'test'

Usar "novo" atribui o valor de "this" dentro da função ao var declarado, enquanto não o usa atribui o valor de retorno.

Stephen Belanger
fonte
2
Não faz sentido usar newqualquer um dos tipos de JavaScript, o que torna a resposta aceita muito mais confusa para iniciantes. text = String('test')e options = {}não serão testados, instanceofmas sim com typeof.
19711 Ryan
3
Date.getTime () // falha. sim, novo é importante.
Stephen Belanger
1
Tente executar isso em um console. Você receberá um erro porque getTime () não existe. Você precisa usar novo.
Stephen Belanger
8

E você pode usá-lo para tratamento de erros e depuração, assim:

try{
    somefunction();
} 
catch(error){
    if (error instanceof TypeError) {
        // Handle type Error
    } else if (error instanceof ReferenceError) {
        // Handle ReferenceError
    } else {
        // Handle all other error types
    }
}
Tarek Saied
fonte
3
//Vehicle is a function. But by naming conventions
//(first letter is uppercase), it is also an object
//constructor function ("class").
function Vehicle(numWheels) {
    this.numWheels = numWheels;
}

//We can create new instances and check their types.
myRoadster = new Vehicle(4);
alert(myRoadster instanceof Vehicle);
yfeldblum
fonte
3

O que é isso?

Javascript é uma linguagem prototípica, o que significa que usa protótipos para 'herança'. o instanceofoperador testa se o tipo de prototypepropriedade de uma função construtora está presente na __proto__cadeia de um objeto. Isso significa que ele fará o seguinte (assumindo que testObj é um objeto de função):

obj instanceof testObj;
  1. Verifique se o protótipo do objeto é igual ao protótipo do construtor: obj.__proto__ === testObj.prototype >> se este for true instanceofretornará true.
  2. Subirá a cadeia de protótipos. Por exemplo: obj.__proto__.__proto__ === testObj.prototype >> se for true instanceofo caso retornará true.
  3. Repetirá a etapa 2 até que o protótipo completo do objeto seja inspecionado. Se em nenhum lugar da cadeia do protótipo do objeto for correspondido testObj.prototype, o instanceofoperador retornará false.

Exemplo:

function Person(name) {
  this.name = name;
}
var me = new Person('Willem');

console.log(me instanceof Person); // true
// because:  me.__proto__ === Person.prototype  // evaluates true

console.log(me instanceof Object); // true
// because:  me.__proto__.__proto__ === Object.prototype  // evaluates true

console.log(me instanceof Array);  // false
// because: Array is nowhere on the prototype chain

Que problemas ele resolve?

Resolveu o problema de verificar convenientemente se um objeto deriva de um determinado protótipo. Por exemplo, quando uma função recebe um objeto que pode ter vários protótipos. Então, antes de usar métodos da cadeia de protótipos, podemos usar o instanceofoperador para verificar se esses métodos estão no objeto.

Exemplo:

function Person1 (name) {
  this.name = name;
}

function Person2 (name) {
  this.name = name;
}

Person1.prototype.talkP1 = function () {
  console.log('Person 1 talking');
}

Person2.prototype.talkP2 = function () {
  console.log('Person 2 talking');
}


function talk (person) {
  if (person instanceof Person1) {
    person.talkP1();
  }
  
  if (person instanceof Person2) {
    person.talkP2();
  }
  
  
}

const pers1 = new Person1 ('p1');
const pers2 = new Person2 ('p2');

talk(pers1);
talk(pers2);

Aqui na talk()função primeiro é verificado se o protótipo está localizado no objeto. Depois disso, o método apropriado é escolhido para execução. Não fazer essa verificação pode resultar na execução de um método que não existe e, portanto, um erro de referência.

Quando é apropriado e quando não?

Nós meio que já repassamos isso. Use-o quando precisar verificar o protótipo de um objeto antes de fazer algo com ele.

Willem van der Veen
fonte
1
Eu acho que você queria escrever algo mais do que " o protótipo de uma função de construtor aparece em algum lugar na propriedade protótipo de um construtor "
Bergi
Você pode mencionar que seria mais apropriado que as pessoas compartilhem uma interface e nomeiem ambos os métodos PersonX.prototype.talk, para que a talkfunção possa simplesmente fazê-lo person.talk().
Bergi 5/09/19
Você estava completamente certo de atualizá-lo, então a definição é melhor. Obrigado por apontar!
Willem van der Veen
Além disso, não use __proto__na documentação, ele está obsoleto - write Object.getPrototype()vez
Bergi
1

Sobre a pergunta "Quando é apropriado e quando não?", Meus 2 centavos:

instanceofraramente é útil no código de produção, mas útil em testes nos quais você deseja afirmar que seu código retorna / cria objetos dos tipos corretos. Por ser explícito sobre os tipos de objetos que seu código está retornando / criando, seus testes se tornam mais poderosos como uma ferramenta para entender e documentar seu código.

Andrew Magee
fonte
1

instanceofé apenas açúcar sintático para isPrototypeOf:

function Ctor() {}
var o = new Ctor();

o instanceof Ctor; // true
Ctor.prototype.isPrototypeOf(o); // true

o instanceof Ctor === Ctor.prototype.isPrototypeOf(o); // equivalent

instanceof depende apenas do protótipo de um construtor de um objeto.

Um construtor é apenas uma função normal. A rigor, é um objeto de função, pois tudo é um objeto em Javascript. E esse objeto de função tem um protótipo, porque toda função tem um protótipo.

Um protótipo é apenas um objeto normal, localizado na cadeia de protótipos de outro objeto. Isso significa que estar na cadeia de protótipos de outro objeto transforma um objeto em um protótipo:

function f() {} //  ordinary function
var o = {}, // ordinary object
 p;

f.prototype = o; // oops, o is a prototype now
p = new f(); // oops, f is a constructor now

o.isPrototypeOf(p); // true
p instanceof f; // true

O instanceofoperador deve ser evitado porque falsifica classes, que não existem em Javascript. Apesar da classpalavra - chave também não no ES2015, já que classé apenas açúcar sintático para ... mas isso é outra história.


fonte
1
A primeira linha está incorreta! Veja aqui para mais informações: "isPrototypeOf () difere da instância do operador. Na expressão" object instanceof AFunction ", a cadeia de protótipos do objeto é verificada no AFunction.prototype, não no próprio AFunction."
11406 Yan Foto
1
@ Yan E ainda instanceofé derivado isPrototypeOf. Eu chamo isso de açúcar sintático. E sua fonte MDN é uma piada, não é?
Não. Não é e não deveria ser uma piada, mas ainda assim o link errado. Eu deveria ter este postado. Eu não quero entrar em detalhes técnicos, mas instanceof não faz a mesma coisa que isPrototypeOf. É isso aí.
12556 Yan Foto
0

Acabei de encontrar um aplicativo do mundo real e o utilizarei com mais frequência agora, eu acho.

Se você usa eventos jQuery, às vezes deseja escrever uma função mais genérica que também pode ser chamada diretamente (sem evento). Você pode instanceofverificar se o primeiro parâmetro da sua função é uma instância jQuery.Evente reagir adequadamente.

var myFunction = function (el) {                
    if (el instanceof $.Event) 
        // event specific code
    else
        // generic code
};

$('button').click(recalc);    // Will execute event specific code
recalc('myParameter');  // Will execute generic code

No meu caso, a função precisava calcular algo para todos (via evento click em um botão) ou apenas um elemento específico. O código que eu usei:

var recalc = function (el) { 
    el = (el == undefined || el instanceof $.Event) ? $('span.allItems') : $(el);
    // calculate...
};
jussty
fonte