Date de première publication : 2015-12-12
Date de mise à jour : 2017-06-07
Licence : CC BY-NC-SA
Ce tutoriel s'adresse à tous les développeurs qui ont déjà les bases de la programmation orienté objet et qui souhaitent découvrir la programmation orientée objet avec les prototypes et les classes en JavaScript.
Ce tutoriel présentera des exemples de code placé côte à côte afin de vous montrer les similitudes entre les différents styles d'écritures, n'hésitez donc pas à comparer les numéros de ligne entre les blocs de code. Vous pouvez aussi copier/coller les exemples de codes et les tester avec votre navigateur préféré.
I. Programmation orientée prototype
Dans l'exemple qui va suivre, nous allons créer une structure - à ne pas confondre avec les struct en C - similaire aux classes en utilisant le mot-clé function. Nous utiliserons le terme « fonction constructeur » pour désigner cette structure.
| Code JavaScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Création d'une fonction constructeur Foo function Foo(arg1) { /* *** ATTRIBUTS *** */ this.attr1 = "One"; this.attr2 = arg1; // METHODE PRIVILEGED this.method1 = function(param1) { console.log(this.attr1 + " " + this.attr2 + " " + param1); }; } // Instanciation de l'objet var myInstance = new Foo("Two"); // Exécuter une méthode myInstance.method1("Three"); |
- Dans certains langages le mot-clé class est utilisé pour créer... des classes ! Mais pas en JavaScript ES5 et versions antérieures. Ici nous avons créé une fonction constructeur.
- Nous avons utilisé le mot-clé this :
- Pour créer nos properties (cf: « attributs »)... Cette syntaxe rappelle le langage Python où nous déclarons des attributs dans le constructeur.
- Pour appeler les attributs à l'intérieur de la méthode method1().
- Toutes les properties peuvent être utilisés par votre instance, à l'image des attributs et méthodes des autres langages utilisant la visibilité public.
Vous ne l'avez peut-être pas encore remarqué mais quelque chose ne va pas dans ce code... nous avons déclaré notre méthode à l'intérieur de la fonction constructeur (cette méthode est aussi appelé une méthode privileged). Une nouvelle copie de method1() sera créée à chaque fois que vous instancierez un objet, cela va par conséquent augmenter la consommation de mémoire.
Pour optimiser cela, créons la méthode method1() grâce au mot-clé prototype :
| Code JavaScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // Création d'une fonction constructeur function Foo(arg1){ /* *** ATTRIBUTS *** */ this.attr1 = "One"; this.attr2 = arg1; /* *** NE PAS DECLARER VOS METHODES ICI !!! *** */ } /* *** DEFINITION DES METHODES *** */ Foo.prototype.method1 = function(param1) { console.log(this.attr1 + " " + this.attr2 + " " + param1); }; // Instanciation de l'objet var myInstance = new Foo("Two"); // Exécuter une méthode myInstance.method1("Three"); |
[spoiler]
| Code JavaScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | // https://tc39.github.io/ecma262/#sec-array.prototype.includes if (!Array.prototype.includes) { Object.defineProperty(Array.prototype, 'includes', { value: function(searchElement, fromIndex) { // 1. Let O be ? ToObject(this value). if (this == null) { throw new TypeError('"this" is null or not defined'); } var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If len is 0, return false. if (len === 0) { return false; } // 4. Let n be ? ToInteger(fromIndex). // (If fromIndex is undefined, this step produces the value 0.) var n = fromIndex | 0; // 5. If n ≥ 0, then // a. Let k be n. // 6. Else n < 0, // a. Let k be len + n. // b. If k < 0, let k be 0. var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); function sameValueZero(x, y) { return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); } // 7. Repeat, while k < len while (k < len) { // a. Let elementK be the result of ? Get(O, ! ToString(k)). // b. If SameValueZero(searchElement, elementK) is true, return true. // c. Increase k by 1. if (sameValueZero(o[k], searchElement)) { return true; } k++; } // 8. Return false return false; } }); } |
Source : https://developer.mozilla.org/en-US/...ludes#Polyfill
II. Héritage par prototype
Créons une nouvelle fonction constructeur ChildFoo qui hérite de Foo :
| Code JavaScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // Création d'une fonction constructeur enfant function ChildFoo(arg1, arg2) { Foo.call(this, arg1); // Appel du constructeur parent this.attr3 = arg2; } /* *** HERITAGE PAR PROTOTYPE *** */ ChildFoo.prototype = Object.create(Foo.prototype); /* *** DEFINITION DES METHODES DE LA FONCTION CONSTRUCTEUR ENFANT *** */ ChildFoo.prototype.method2 = function(param1, param2) { console.log(this.attr1 + " " + this.attr2 + " " + this.attr3 + " " + param1 + " " + param2); }; // Instanciation de l'objet var c = new ChildFoo("Two", "Three"); // Exécuter une méthode c.method1("Three"); // Hérité de Foo c.method2("Four", "Five"); |
- Nous appelons le constructeur parent avec la fonction call(). Ceci est similaire au super en Java ou base en C#. Le premier paramètre doit être this.
- Nous réalisons un héritage par prototype avec Object.create(prototype). D'après le MDN, pour Internet Explorer, Object.create() est seulement disponible à partir de IE9.
Si vous souhaitez faire de l'héritage avec les anciens navigateurs, il existe une solution alternative : remplacer ChildFoo.prototype = Object.create(Foo.prototype); par ChildFoo.prototype = new Foo(null);
Cette astuce possède néanmoins un inconvénient : le contenu de votre fonction constructeur parent sera appelé.
Cette astuce possède néanmoins un inconvénient : le contenu de votre fonction constructeur parent sera appelé.
III. Programmation orientée objet avec les classes (ES6)
ES6 apporte un nouveau style d'écriture avec les « classes », plus familier pour certains développeurs, mais qui finalement n'est qu'un sucre syntaxique des prototypes.
Les exemples ci-dessous peuvent ne pas fonctionner si votre navigateur ne supporte pas ES6 mais vous pouvez toujours tester avec un transpiler tel que Babel
|
|
Héritage :
|
|
IV. Création d'objet
JavaScript propose divers styles d'écritures pour créer des objets. Le style préféré d'un développeur JS reste celle du literal object, plus court et plus concis.
|
|
|
Il est même possible de créer un objet, puis de lui ajouter des properties :
| Code JavaScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var anonymousInstance = undefined; // Création d'un objet sans properties (5 styles d'écritures équivalents) anonymousInstance = {}; anonymousInstance = new Object(); anonymousInstance = Object.create(Object.prototype); anonymousInstance = new function() {}; anonymousInstance = new class {}; // ES6 only // Création des properties anonymousInstance.a = 1; anonymousInstance.b = 2; anonymousInstance.c = function() { // Code... }; |
- Nous avons utilisé différents styles d'écriture pour créer des objets mais ils sont tous équivalents, ils ont tous Object pour prototype.
- Si vous souhaitez créer un objet qui ne possède aucun prototype, vous pouvez le créer à partir de l'instruction suivante : Object.create(null);
- Nous n'avons pas utilisé de fonction nommé pour créer ces objets, nous avons créé des objets à usage unique (ex: un message JSON envoyé au serveur). Il n'est pas nécessaire d'avoir recours aux prototypes pour créer des méthodes pour ce type de cas, même s'il est toujours possible d'utiliser les prototypes pour définir des méthodes :
|
|
V. Visibilité et éléments statiques
Dans cette partie nous allons voir comment créer des attributs privés (private), des méthodes privileged (vous en avez déjà vu dans la première partie), et des éléments statiques.
|
|
Utilisation :
| Code JavaScript : | Sélectionner tout |
1 2 3 4 5 | var o = new Bar(2, 4); console.log(o.privilegedMethod()); console.log(o.publicAttribute); console.log(Bar.staticAttribute); // ES5 only console.log(Bar.staticMethod()); |
Que pouvons-nous dire à propos de cet exemple de code ?
- Nous avons utilisé le mot-clé var pour déclarer une variable qui n'est accessible qu'à l'intérieur de notre function. On peut considérer cela comme l'équivalent des attributs/méthodes ayant une visibilité private dans les autres langages. Il n'est pas possible de créer d'attribut private avec les classes ES6.
- Une méthode privileged est une méthode publique déclarée à l'intérieur d'une fonction constructeur, elle peut contenir des variables (cf: des éléments private) et des properties (cf: des éléments public).
- Nous n'utilisons pas le mot-clé prototype pour déclarer les éléments statiques, nous créons directement une property à notre function. Comme dans les autres langages, une méthode static ne peut pas accéder aux éléments d'une instance (cf: this). Il n'est pas possible de créer des attributs static avec les classes ES6, peut-être dans une version ultérieure.
Vous êtes arrivé à la fin de ce mini-tutoriel pour créer des structures et instancier des objets en JavaScript ES5 ou ES6. N'hésitez pas à consulter mon profil et mon site (https://gokan-ekinci.appspot.com/) pour plus d'infos.
Vous avez lu gratuitement 139 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.