Auteur : Gokan EKINCI
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.
JavaScript est un langage orienté prototype, qui est une forme de forme forme de programmation orienté objet, il est donc possible d'instancier des objets et de faire de l'héritage. Je vais dans un premier temps vous présenter comment créer des objets et faire de l'héritage avec les prototypes, puis vous présenter une syntaxe alternative avec les classes introduites dans la spécification ECMAScript 6 (pour information ES6 a été validé et publié le 17 juin 2015).
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.
// 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");
Que pouvons-nous dire à propos de cet exemple de code ?
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 :
// 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");
Nous observons ici l'une des puissances de la programmation orientée prototype. JavaScript offre la possibilité d'ajouter des méthodes à tout moment en dehors de la fonction constructeur, cela permet notamment de créer des fonctions polyfills pour émuler des fonctions standard qui ne sont pas implémentés dans les anciens navigateurs. Par exemple la récente fonction Array.prototype.includes(), qui sert à vérifier si un élément est bien présent dans un tableau, n'est pas disponible sous la plupart des navigateurs, il est possible de vérifier l'existence d'une property, puis d'ajouter la fonctionnalité si elle n'existe pas (cliquez pour dévoiler le spoiler) :
// 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, searchElement)) {
return true;
}
k++;
}
// 8. Return false
return false;
}
});
}
Source : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#Polyfill
II. Héritage par prototype
Créons une nouvelle fonction constructeur ChildFoo qui hérite de Foo :
// 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");
Que pouvons-nous dire à propos de cet exemple de code ?
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é.
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
Programmation orientée objet avec les prototypes (ES5/ES6)
Programmation orientée objet avec les classes (ES6 only)
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);
};
class Foo {
constructor(arg1){
/* *** ATTRIBUTS *** */
this.attr1 = "One";
this.attr2 = arg1;
}
/* *** DEFINITION DES METHODES *** */
method1(param1) {
console.log(this.attr1 + " " + this.attr2 + " " + param1);
}
}
Héritage :
Programmation orientée objet avec les prototypes (ES5/ES6)
Programmation orientée objet avec les classes (ES6 only)
function ChildFoo(arg1, arg2) {
Foo.call(this, arg1); // Appel du constructeur parent
this.attr3 = arg2;
}
/* *** HERITAGE PAR PROTOTYPE *** */
ChildFoo.prototype = Object.create(Foo.prototype);
class ChildFoo extends Foo {
constructor(arg1, arg2){
super(arg1); // Appel du constructeur parent
this.attr3 = arg2;
}
}
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.
Style literal object (ES5/ES6)
Style `new function` (ES5/ES6)
Style anonymous class (ES6 only)
var anonymousInstance = {
a:1,
b:2,
c:function(){
// Code...
}
};
anonymousInstance.c();
var anonymousInstance = new function() {
this.a = 1;
this.b = 2;
this.c = function(){
// Code...
};
};
anonymousInstance.c();
var anonymousInstance = new class {
constructor(){
this.a = 1;
this.b = 2;
this.c = function(){
// Code...
};
}
};
anonymousInstance.c();
Il est même possible de créer un objet, puis de lui ajouter des properties :
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...
};
Que pouvons-nous dire à propos de ces exemples de code ?
Style `Object.getPrototypeOf` (requiert IE9 ou plus)
Style `__proto__` (requiert un navigateur compatible ES6)
Object.getPrototypeOf(anonymousInstance).method2 = function() {
console.log("method2 !!!");
};
anonymousInstance.__proto__.method2 = function() {
console.log("method2 !!!");
};
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.
Programmation orientée objet avec les prototypes (ES5/ES6)
Programmation orientée objet avec les classes (ES6 only)
// Création d'une fonction constructeur
function Bar(arg1, arg2){
// ATTRIBUT PRIVATE
var privateAttribute = arg1;
// ATTRIBUT PUBLIC
this.publicAttribute = arg2;
// METHODE PRIVILEGED
this.privilegedMethod = function() {
return privateAttribute + this.publicAttribute;
};
}
// ATTRIBUT PUBLIC STATIC
Bar.staticAttribute = "Static attribute !";
// METHODE PUBLIC STATIC
Bar.staticMethod = function() {
return "Static method !";
};
// Création d'une classe
class Bar {
constructor (arg1, arg2) {
// ATTRIBUT PRIVATE
// PAS D'ATTRIBUT PRIVATE AVEC LES CLASSES ES6
// ATTRIBUT PUBLIC
this.publicAttribute = arg2;
// METHODE PRIVILEGED
this.privilegedMethod = function() {
return this.publicAttribute;
};
}
// ATTRIBUT PUBLIC STATIC
// PAS D'ATTRIBUT STATIC AVEC LES CLASSES ES6
// METHODE PUBLIC STATIC
static staticMethod () {
return "Static method !";
}
}
Utilisation :
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 ?
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 136 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.