Qu'est-ce que l'opérateur instanceof en JavaScript?

313

Le instanceofmot clé en JavaScript peut être assez déroutant lorsqu'il est rencontré pour la première fois, car les gens ont tendance à penser que JavaScript n'est pas un langage de programmation orienté objet.

  • Qu'Est-ce que c'est?
  • Quels problèmes cela résout-il?
  • Quand est-ce approprié et quand non?
Alon Gubkin
la source
3
Bien que les réponses ci-dessous soient très utiles, vous ne les utilisez pas beaucoup (du moins je ne le fais pas) dans les applications de mots réels. On ferait une propriété object.type qui contient une chaîne et la vérifie.
Omar Al-Ithawi
4
JS n'a aucun sens: "foo" instanceof String=> faux, 1 instanceof Number=> faux, {} instanceof Object=> faux. Tu peux répéter s'il te plait?!
morbusg
12
@morbusg votre commentaire est trompeur. Le premier "foo" instanceof String => falseest correct, car typeof "foo" == 'string'. new String("foo") instanceof String => true, car typeof String == 'function'- vous devez traiter la fonction comme une classe (définition de classe). La variable devient instanceofune function(classe) lorsque vous l'assignez en tant que var v = new AnythingWhatTypeofEqualsFunction(). La même chose s'applique à 1. typeof 1 == 'number'- 'nombre' n'est pas 'fonction' :) Suivant - {} instanceof Objectest TRUEdans le nœud et les navigateurs modernes
fider
1
@fider: C'était un commentaire sur la spécification de langue, venant d'un rubyist.
morbusg
@morbusg - ({}) instanceof Objectreviendra true. En fait, le code que vous avez écrit vous donnera une erreur.
DDM

Réponses:

279

exemple de

L'opérande côté gauche (LHS) est l'objet réel testé sur l'opérande côté droit (RHS) qui est le constructeur réel d'une classe. La définition de base est:

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

Voici quelques bons exemples et voici un exemple tiré directement du site de développeur de 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)

Une chose à noter est instanceofévaluée à true si l'objet hérite du prototype de la classe:

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

C'est p instanceof Personvrai puisque phérite de Person.prototype.

À la demande du PO

J'ai ajouté un petit exemple avec un exemple de code et une explication.

Lorsque vous déclarez une variable, vous lui donnez un type spécifique.

Par exemple:

int i;
float f;
Customer c;

Le vous montrer ci - dessus certaines variables, à savoir i, fet c. Les types sont integer, floatet défini par l' utilisateur Customertype de données. Les types tels que ci-dessus peuvent être pour n'importe quelle langue, pas seulement JavaScript. Cependant, avec JavaScript lorsque vous déclarez une variable, vous ne définissez pas explicitement un type var x, x peut être un nombre / chaîne / un type de données défini par l'utilisateur. Donc, qu'est instanceof- ce qu'il vérifie l'objet pour voir s'il est du type spécifié, Customernous pouvons donc faire de l' objet en haut :

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!

Ci-dessus, nous avons vu que cela a cété déclaré avec le type Customer. Nous l'avons nouvellement vérifié et vérifié s'il est de type Customerou non. Bien sûr, cela revient vrai. Puis en utilisant toujours l' Customerobjet, nous vérifions s'il s'agit d'un String. Non, certainement pas un Stringnouvel Customerobjet, pas un Stringobjet. Dans ce cas, elle renvoie false.

C'est aussi simple que ça!

JonH
la source
@Alon - J'ai ajouté un exemple pour vous. Voir ci-dessus, color1 est de type chaîne, donc quand vous dites que color1 instanceof String;cela retournera vrai car color1 est une chaîne.
JonH
@Alon - Il vérifie un objet pour voir de quel type d'objet il s'agit. Considérez un objet personne / client. Donc person p = new person()p est maintenant un type de personne et non un type de chaîne.
JonH
En JavaScript, il peut être difficile de comprendre qu'une variable a la flexibilité d'avoir un type différent tout au long de sa durée de vie. Exemple de code: jsfiddle.net/sikusikucom/znSPv
moey
@ Siku-Siku.Com - Je ne vois aucune astuce pour que var zero = 0; alert(zero); zero = "0"; alert(zero)nous passions d'une primitive intà une primitive stringsans aucun problème.
JonH
Oh, je voulais dire qu'il était difficile / nouveau de comprendre (ne pas faire ) que la "flexibilité" en JavaScript, en particulier. pour certains qui sont habitués à un langage qui nécessite par exemple un transtypage explicite pour modifier un type de variable. Comme vous l'avez souligné, il est très facile de changer le type, par exemple primitif inten primitif string.
moey
95

Il y a une facette importante de l'instance de cela qui ne semble pas être couverte dans aucun des commentaires jusqu'à présent: l'héritage. Une variable en cours d'évaluation à l'aide d'instanceof pourrait retourner vrai pour plusieurs "types" en raison de l'héritage prototypique.

Par exemple, définissons un type et un sous-type:

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;

Maintenant que nous avons quelques "classes", faisons quelques instances et découvrons de quoi il s'agit:

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

Vous voyez cette dernière ligne? Tous les "nouveaux" appels à une fonction renvoient un objet qui hérite d'Object. Cela est vrai même lorsque vous utilisez un raccourci de création d'objet:

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

Et qu'en est-il des définitions de "classe" elles-mêmes? De quoi sont-ils des exemples?

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

Je pense qu'il est important de comprendre que tout objet peut être une instance de types MULTIPLES, car vous pouvez (à tort) supposer que vous pourriez faire la différence entre, disons et objet et une fonction en utilisant instanceof. Comme ce dernier exemple montre clairement qu'une fonction est un objet.

Ceci est également important si vous utilisez des modèles d'héritage et souhaitez confirmer la descendance d'un objet par des méthodes autres que le typage canard.

J'espère que cela aide quiconque à explorer instanceof.

webnesto
la source
Encore plus impressionnant, c'est qu'après avoir hérité du prototype avec SubFoo.prototype = new Foo();vous pouvez ajouter plus de méthodes, et le subfoo instanceof Foocontrôle passera toujours ainsisubfoo instanceof SubFoo
Cory Danielson
87

Les autres réponses ici sont correctes, mais elles n'abordent pas le fonctionnement instanceofréel, ce qui peut intéresser certains juristes spécialisés en langues.

Chaque objet en JavaScript a un prototype, accessible via la __proto__propriété. Les fonctions ont également une prototypepropriété, qui est l'initiale __proto__de tout objet créé par elles. Lorsqu'une fonction est créée, elle reçoit un objet unique pour prototype. L' instanceofopérateur utilise cette unicité pour vous donner une réponse. Voici à quoi instanceofpourrait ressembler si vous l'avez écrit en tant que fonction.

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__;
  }
}

Il s'agit essentiellement de paraphraser l'ECMA-262 édition 5.1 (également connu sous le nom ES5), section 15.3.5.3.

Notez que vous pouvez réaffecter n'importe quel objet à la prototypepropriété d' une fonction et vous pouvez réaffecter la __proto__propriété d' un objet après sa construction. Cela vous donnera des résultats intéressants:

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
la source
5
Il convient de noter que la manipulation directe de la __proto__propriété " " n'est pas autorisée dans IE. Si je me souviens bien, la manipulation directe de la propriété n'est pas non plus incluse dans la spécification ECMA, donc c'est probablement une mauvaise idée de l'utiliser pour une affectation autre que dans des activités académiques.
webnesto
@webnesto, c'est vrai, proto n'est pas dans la spécification. Je ne savais pas qu'IE ne le supportait pas cependant. Quand vous dites manipulation directe, voulez-vous dire qu'il n'est pas du tout exposé au code JS, ou qu'il n'est tout simplement pas accessible en écriture?
Jay Conrod
2
Pas sûr à 100% sur les anciennes versions. On dirait d'ici ( stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10 ) que dans IE9, il est au moins lisible (mais pas mutable). Il convient également de noter qu'il semble que les navigateurs le suppriment complètement.
webnesto
4
Il est important de comprendre l'existence implicite de la propriété proto , qu'elle soit accessible au code utilisateur ou non. +10 si je pouvais pour citer la spécification, c'est exactement ce que je suis venu chercher ici.
Mike Edwards
3
Afin d'obtenir le lien prototype, utilisez-le Object.getPrototypeOf(o), ce sera le même que celui que __proto__vous décrivez, mais conforme à ECMAScript.
froginvasion
45

Je pense qu'il vaut la peine de noter que instanceof est défini par l'utilisation du "nouveau" mot clé lors de la déclaration de l'objet. Dans l'exemple 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)

Ce qu'il n'a pas mentionné, c'est ceci;

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

La spécification de "nouveau" a en fait copié l'état final de la fonction constructeur String dans la var color1, plutôt que de simplement la définir sur la valeur de retour. Je pense que cela montre mieux ce que fait le nouveau mot clé;

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'

L'utilisation de "new" affecte la valeur de "this" à l'intérieur de la fonction à la variable déclarée, tandis que ne pas l'utiliser affecte la valeur de retour à la place.

Stephen Belanger
la source
2
Cela n'a aucun sens à utiliser newavec l'un des types de JavaScript, ce qui rend la réponse acceptée beaucoup plus déroutante pour les débutants. text = String('test')et options = {}ne vont pas être testés par, instanceofmais plutôt avec typeof.
Ryan
3
Date.getTime () // échoue. oui, nouveau est important.
Stephen Belanger
1
Essayez de l'exécuter dans une console. Vous obtiendrez une erreur car getTime () n'existe pas. Vous avez besoin d'utiliser nouveau.
Stephen Belanger
8

Et vous pouvez l'utiliser pour la gestion des erreurs et le débogage, comme ceci:

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

Qu'Est-ce que c'est?

Javascript est un langage prototypique qui signifie qu'il utilise des prototypes pour «l'héritage». l' instanceofopérateur teste si le type de prototypepropriété d' une fonction constructeur est présent dans la __proto__chaîne d'un objet. Cela signifie qu'il fera ce qui suit (en supposant que testObj est un objet fonction):

obj instanceof testObj;
  1. Vérifiez si le prototype de l'objet est égal au prototype du constructeur: obj.__proto__ === testObj.prototype >> si cela true instanceofreviendra true.
  2. Montera la chaîne prototype. Par exemple: obj.__proto__.__proto__ === testObj.prototype >> si c'est true instanceofretourne true.
  3. Répétera l'étape 2 jusqu'à ce que le prototype complet de l'objet soit inspecté. Si nulle part sur la chaîne de prototype de l'objet est mis en correspondance avec testObj.prototypealors l' instanceofopérateur retournera false.

Exemple:

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

Quels problèmes cela résout-il?

Il a résolu le problème de vérifier commodément si un objet dérive d'un certain prototype. Par exemple, lorsqu'une fonction reçoit un objet qui peut avoir différents prototypes. Ensuite, avant d'utiliser les méthodes de la chaîne de prototypes, nous pouvons utiliser l' instanceofopérateur pour vérifier si ces méthodes se trouvent sur l'objet.

Exemple:

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);

Ici dans le talk() fonction, on vérifie d'abord si le prototype est situé sur l'objet. Après cela, la méthode appropriée est choisie pour s'exécuter. Ne pas faire cette vérification pourrait entraîner l'exécution d'une méthode qui n'existe pas et donc une erreur de référence.

Quand est-ce approprié et quand non?

Nous sommes déjà passés par là. Utilisez-le lorsque vous avez besoin de vérifier le prototype d'un objet avant d'en faire quelque chose.

Willem van der Veen
la source
1
Je pense que vous vouliez écrire autre chose que " le prototype d'une fonction constructeur apparaît quelque part dans la propriété prototype d'un constructeur "
Bergi
Vous voudrez peut-être mentionner qu'il serait plus approprié que les personnes partagent une interface et nomment ces deux méthodes PersonX.prototype.talk, afin que la talkfonction puisse simplement faire person.talk().
Bergi
Vous aviez complètement raison de le mettre à jour, donc la définition est meilleure. Merci d'avoir souligné!
Willem van der Veen
Aussi, ne pas utiliser __proto__dans la documentation, c'est obsolète - écrivez à la Object.getPrototype()place
Bergi
1

Sur la question "Quand est-ce approprié et quand non?", Mes 2 cents:

instanceofest rarement utile dans le code de production, mais utile dans les tests où vous voulez affirmer que votre code retourne / crée des objets des types corrects. En étant explicite sur les types d'objets que votre code retourne / crée, vos tests deviennent plus puissants comme outil pour comprendre et documenter votre code.

Andrew Magee
la source
1

instanceofest juste du sucre syntaxique pour 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 dépend juste du prototype d'un constructeur d'un objet.

Un constructeur n'est qu'une fonction normale. À proprement parler, c'est un objet fonction, car tout est un objet en Javascript. Et cet objet fonction a un prototype, car chaque fonction a un prototype.

Un prototype est juste un objet normal, qui est situé dans la chaîne de prototype d'un autre objet. Cela signifie que le fait d'être dans la chaîne de prototypes d'un autre objet fait d'un objet un prototype:

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

L' instanceofopérateur doit être évité car il simule des classes qui n'existent pas en Javascript. Malgré le classmot - clé qui n'est pas non plus dans ES2015, puisque classc'est encore du sucre syntaxique pour ... mais c'est une autre histoire.


la source
1
La toute première ligne est incorrecte! Voir ici pour en savoir plus: "isPrototypeOf () diffère de l'opérateur instanceof. Dans l'expression" objet instanceof AFunction ", la chaîne de prototype d'objet est vérifiée par rapport à AFunction.prototype, et non par AFunction elle-même."
Yan Foto
1
@Yan Et instanceofest toujours dérivé de isPrototypeOf. J'appelle ce sucre syntaxique. Et votre source MDN est une blague, n'est-ce pas?
Non, ce n'est pas et n'était pas censé être une blague, mais toujours le mauvais lien. J'étais censé faire publier celui-ci . Je ne veux pas entrer dans les détails techniques, mais instanceof ne fait pas la même chose que isPrototypeOf. C'est tout.
Yan Foto
0

Je viens de trouver une application réelle et je vais l'utiliser plus souvent maintenant, je pense.

Si vous utilisez des événements jQuery, vous souhaitez parfois écrire une fonction plus générique qui peut également être appelée directement (sans événement). Vous pouvez utiliser instanceofpour vérifier si le premier paramètre de votre fonction est une instance de jQuery.Eventet réagir de manière appropriée.

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

Dans mon cas, la fonction avait besoin de calculer quelque chose soit pour tous (via l'événement click sur un bouton), soit pour un seul élément spécifique. Le code que j'ai utilisé:

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