web-dev-qa-db-fra.com

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

Le mot clé instanceof 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?
299
Alon Gubkin

exemple de

L'opérande côté gauche (LHS) est l'objet actuellement testé sur l'opérande côté droit (RHS) qui est le constructeur effectif 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 de site de développement 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 que instanceof est évalué à true si l'objet hérite du prototype de la classe:

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

C'est-à-dire que p instanceof Person est vrai puisque p hérite de Person.prototype.

À la demande de l'OP

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

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

Par exemple:

int i;
float f;
Customer c;

Ce qui précède vous montre quelques variables, à savoir i, f et c. Les types sont integer, float et un type de données défini par l'utilisateur Customer. Des types tels que ceux ci-dessus pourraient ê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/une chaîne/un type de données défini par l'utilisateur. Donc, ce que instanceof vérifie l'objet pour voir s'il est du type spécifié. Ainsi, en prenant l'objet précédent Customer, nous pourrions le faire:

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 c était déclaré avec le type Customer. Nous l'avons à nouveau et vérifié s'il est de type Customer ou non. Bien sûr, ça retourne vrai. Puis, en utilisant toujours l’objet Customer, nous vérifions s’il s’agit d’un String. Non, certainement pas un String nous avons créé un objet Customer et non un objet String. Dans ce cas, il retourne faux.

C'est aussi simple que ça!

260
JonH

Il existe une facette importante dans un exemple de ce qui ne semble pas être couvert par aucun des commentaires jusqu'à présent: l'héritage. Une variable évaluée à l'aide d'instanceof pourrait renvoyer true pour plusieurs "types" en raison d'un héritage de prototype.

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 à quoi elles correspondent:

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

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

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

Et que dire 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 plusieurs types est important, puisque vous supposez (à tort) que vous pouvez différencier, par exemple, et objet et une fonction en utilisant instanceof. Comme ce dernier exemple montre clairement une fonction est un objet.

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

Espérons que cela aide tous ceux qui explorent instanceof.

92
webnesto

Les autres réponses ici sont correctes, mais elles ne décrivent pas comment instanceof fonctionne réellement, ce qui peut intéresser certains avocats spécialistes des langues.

Chaque objet en JavaScript a un prototype, accessible via la propriété __proto__. Les fonctions ont également une propriété prototype, qui est le __proto__ initial pour tous les objets créés par elles. Lorsqu'une fonction est créée, un objet unique lui est attribué pour prototype. L'opérateur instanceof utilise cette unicité pour vous donner une réponse. Voici à quoi pourrait ressembler instanceof si vous l’écriviez sous forme de 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__;
  }
}

Ceci est essentiellement une paraphrase de l'ECMA-262 édition 5.1 (également appelée ES5), section 15.3.5.3.

Notez que vous pouvez réaffecter n'importe quel objet à la propriété prototype d'une fonction, et vous pouvez réaffecter la propriété __proto__ 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
86
Jay Conrod

Je pense qu'il est intéressant de noter que l'instanceof est définie par l'utilisation du mot clé "new" 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é est ceci;

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

En spécifiant "new", l'état final de la fonction constructeur de chaîne a été copié dans la variable color1, plutôt que de le définir comme 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 son utilisation n'affecte pas la valeur renvoyée.

45
Stephen Belanger

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
    }
}
8
Tarek Saied

Qu'Est-ce que c'est?

Javascript est un langage prototypique, ce qui signifie qu'il utilise des prototypes pour "hériter". l'opérateur instanceof teste si le type de propriété prototype d'un constructeur est présent dans la chaîne __proto__ d'un objet. Cela signifie qu'il procédera comme suit (en supposant que testObj est un objet de fonction):

obj instanceof testObj;
  1. Vérifiez si le prototype de l'objet est égal au prototype du constructeur: obj.__proto__ === testObj.prototype >> s'il s'agit de trueinstanceof retournera true.
  2. Va grimper la chaîne de prototypes. Par exemple: obj.__proto__.__proto__ === testObj.prototype >> s'il s'agit de trueinstanceof retournera true.
  3. Répétez 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 ne correspond à testObj.prototype, l'opérateur instanceof 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?

Cela résolvait le problème de vérifier si un objet dérive d'un certain prototype. Par exemple, quand une fonction reçoit un objet pouvant avoir différents prototypes. Ensuite, avant d'utiliser les méthodes de la chaîne de prototypes, nous pouvons utiliser l'opérateur instanceof pour vérifier si ces méthodes sont 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 la fonction talk(), on vérifie d'abord si le prototype est situé sur l'objet. Après cela, la méthode appropriée est choisie pour être exécutée. Ne pas effectuer cette vérification peut 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 avons en quelque sorte déjà examiné cela. Utilisez-le lorsque vous avez besoin de vérifier le prototype d'un objet avant de faire quelque chose avec.

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

Sur la question "Quand est-ce approprié et quand ne l'est-il pas?", Mes 2 centimes:

instanceof est rarement utile dans le code de production, mais utile dans les tests dans lesquels vous souhaitez affirmer que votre code renvoie/crée des objets des types corrects. En expliquant les types d'objets que votre code renvoie/crée, vos tests deviennent plus puissants en tant qu'outil de compréhension et de documentation de votre code.

1
Andrew Magee

instanceof n'est qu'un 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 ne dépend que du prototype d'un constructeur d'objet.

Un constructeur n'est qu'une fonction normale. À proprement parler, il s'agit d'un objet fonction, puisque tout est un objet en Javascript. Et cet objet de fonction a un prototype, car chaque fonction a un prototype.

Un prototype est simplement un objet normal situé dans la chaîne de prototypes d'un autre objet. Cela signifie qu’être dans la chaîne de prototypes d’un autre objet crée un objet pour 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'opérateur instanceof devrait être évité car il simule des classes, qui n'existent pas en Javascript. Malgré le mot clé class, pas plus dans ES2015, car class n'est qu'une fois de plus un sucre syntaxique pour ... mais c'est une autre histoire.

1
user6445533

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 instanceof pour vérifier si le premier paramètre de votre fonction est une instance de jQuery.Event et réagir correctement.

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 devait calculer quelque chose soit pour tous (via l'événement de clic 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...
};
0
jussty