diff options
author | Florian Merz <me@fiji-flo.de> | 2021-02-11 12:36:08 +0100 |
---|---|---|
committer | Florian Merz <me@fiji-flo.de> | 2021-02-11 12:36:08 +0100 |
commit | 39f2114f9797eb51994966c6bb8ff1814c9a4da8 (patch) | |
tree | 66dbd9c921f56e440f8816ed29ac23682a1ac4ef /files/fr/web/javascript/guide/details_of_the_object_model | |
parent | 8260a606c143e6b55a467edf017a56bdcd6cba7e (diff) | |
download | translated-content-39f2114f9797eb51994966c6bb8ff1814c9a4da8.tar.gz translated-content-39f2114f9797eb51994966c6bb8ff1814c9a4da8.tar.bz2 translated-content-39f2114f9797eb51994966c6bb8ff1814c9a4da8.zip |
unslug fr: move
Diffstat (limited to 'files/fr/web/javascript/guide/details_of_the_object_model')
-rw-r--r-- | files/fr/web/javascript/guide/details_of_the_object_model/index.html | 747 |
1 files changed, 747 insertions, 0 deletions
diff --git a/files/fr/web/javascript/guide/details_of_the_object_model/index.html b/files/fr/web/javascript/guide/details_of_the_object_model/index.html new file mode 100644 index 0000000000..50a78fdf27 --- /dev/null +++ b/files/fr/web/javascript/guide/details_of_the_object_model/index.html @@ -0,0 +1,747 @@ +--- +title: Le modèle objet JavaScript en détails +slug: Web/JavaScript/Guide/Le_modèle_objet_JavaScript_en_détails +tags: + - Guide + - Intermediate + - JavaScript + - Object +translation_of: Web/JavaScript/Guide/Details_of_the_Object_Model +--- +<p>{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Utiliser_les_objets", "Web/JavaScript/Guide/Utiliser_les_promesses")}}</p> + +<p class="summary">JavaScript est un langage objet basé sur des prototypes plus que sur des classes. Cette différence peut rendre difficile la compréhension des hiérarchies entre objets, leurs créations, l’héritage de propriétés et de leurs valeurs... L’objectif de ce chapitre est de clarifier ces différents éléments en expliquant le modèle prototypal et ses différences avec un système de classes.</p> + +<p>Avant de lire ce chapitre, il est conseillé d’avoir quelques bases en JavaScript, notamment en ayant déjà écrit quelques fonctions et manipulé des objets.</p> + +<h2 id="Langages_de_prototypes_Langages_de_classes">Langages de prototypes / Langages de classes</h2> + +<p>Les langages orientés objet basés sur des classes, comme Java ou C++, se fondent sur deux entités principales distinctes : les classes et les instances.</p> + +<ul> + <li>Une <em>classe</em> définit l’ensemble des propriétés (que ce soit les méthodes et les attributs en Java, ou les membres en C++) caractérisant un certain ensemble d’objets. Une classe est une représentation abstraite et non pas la représentation particulière d'un membre de cet ensemble d'objets. Par exemple, la classe <code>Employé</code> permettrait de représenter l’ensemble de tous les employés.</li> + <li>Une <em>instance</em> correspond à l’instanciation d'une classe. C’est un de ses membres. Ainsi,<code> Victoria</code> pourrait être une instance de la classe <code>Employé</code> et représenterait un individu en particulier comme un employé. Une instance possède exactement les mêmes propriétés que sa classe (ni plus ni moins).</li> +</ul> + +<p>Un langage basé sur des prototypes, comme JavaScript, n’utilise pas cette distinction. Il ne possède que des objets. On peut avoir des objets <em>prototypiques</em> qui sont des objets agissant comme un modèle sur lequel on pourrait obtenir des propriétés initiales pour un nouvel objet. Tout objet peut définir ses propres propriétés, que ce soit à l’écriture ou pendant l’exécution. De plus, chaque objet peut être associé comme le <em>prototype</em> d’un autre objet, auquel cas le second objet partage les propriétés du premier.</p> + +<h3 id="La_définition_dune_classe">La définition d'une classe</h3> + +<p>Dans les langages de classes, on doit définir une classe dans une <em>définition de classe</em>. Dans cette définition, on peut inclure certaines méthodes spéciales, comme les <em>constructeurs</em> qui permettent de créer de nouvelles instances de cette classe. Un constructeur permet de définir certaines valeurs initiales pour des propriétés de l’instance et d’effectuer certains traitements lors de la création d’une instance. L’opérateur <code>new</code>, utilisé avec le constructeur, permet de créer de nouvelles instances.</p> + +<p>Le fonctionnement de JavaScript est similaire. En revanche, il n’y a pas de différence entre la définition de la classe et le constructeur. La fonction utilisée pour le constructeur permet de créer un objet avec un ensemble initial de propriétés et de valeurs. Toute fonction JavaScript peut être utilisée comme constructeur. L’opérateur <code>new</code> doit être utilisé avec un constructeur pour créer un nouvel objet.</p> + +<div class="blockIndicator note"> +<p><strong>Note :</strong> Bien qu'ECMAScript 2015 introduise <a href="/fr/docs/Web/JavaScript/Reference/Classes">une déclaration de classe</a>, celle-ci n'est qu'un sucre syntaxique utilisant l'héritage à base de prototype. Cette nouvelle syntaxe n'introduit pas de nouveau paradigme d'héritage objet au sein de JavaScript.</p> +</div> + +<h3 id="Classes-filles_et_héritage">Classes-filles et héritage</h3> + +<p>Dans un langage de classes, on peut créer une hiérarchie de classes à travers la définition de classe. En effet, dans cette définition, on peut préciser si la nouvelle classe est une classe-fille d'une classe existante. La classe-fille hérite alors des propriétés de la classe-parente et peut ajouter de nouvelles propriétés ou modifier les propriétés héritées. Si, par exemple, la classe <code>Employé</code> comprend les propriétés <code>nom</code> et <code>branche</code> et que <code>Manager</code> est une classe-fille de la classe <code>Employee</code> qui ajoute la propriété <code>rapports</code>. Dans cet exemple, une instance de la classe <code>Manager</code> aurait trois propriétés : <code>nom</code>, <code>branche</code>, et <code>rapports</code>.</p> + +<p>En JavaScript, l’héritage permet d’associer un objet prototypique avec n’importe quel constructeur. Ainsi, on peut créer le même exemple <code>Employé</code> — <code>Manager</code> mais on utilisera une terminologie légèrement différente. Tout d’abord, on définit le constructeur (fonction) <code>Employé</code> et on y définit les propriétés <code>nom</code> et <code>branche</code>. Ensuite, on définit le constructeur <code>Manager</code> avec la propriété <code>rapports</code>. Enfin, on assigne un nouvel objet <code>Employé</code> comme <code>prototype</code> dans le constructeur <code>Manager</code>. Ainsi, quand on créera un nouvel objet <code>Manager</code>, il héritera des propriétés <code>nom</code> et <code>branche</code> de l’objet <code>Employé</code>.</p> + +<h3 id="Ajouter_ou_retirer_des_propriétés">Ajouter ou retirer des propriétés</h3> + +<p>Dans un langage de classe, les classes sont créées durant la compilation et les instanciations de la classe ont lieu durant la compilation ou durant l’exécution. Il n’est pas possible de modifier les propriétés (leur quantité, leurs types) une fois que la classe a été définie. JavaScript, en revanche, permet d’ajouter ou de retirer des propriétés à n’importe quel objet pendant l’exécution. Si une propriété est ajoutée à un objet utilisé comme prototype, tous les objets qui l’utilisent comme prototype bénéficieront de cette propriété.</p> + +<h3 id="Résumé_des_différences">Résumé des différences</h3> + +<p>Le tableau suivant fournit un rapide récapitulatif de ces différences. Le reste du chapitre décrira l’utilisation de constructeur et de prototypes en JavaScript ainsi que la méthode correspondante qui pourrait être utilisée en Java.</p> + +<table class="standard-table"> + <thead> + <tr> + <th scope="col">Catégorie</th> + <th scope="col">Langage de classe (Java)</th> + <th scope="col">Langage de prototype (JavaScript)</th> + </tr> + </thead> + <tbody> + <tr> + <td>Classe ou instance</td> + <td>Les classes et les instances sont deux entités distinctes.</td> + <td>Tous les objets sont des instances.</td> + </tr> + <tr> + <td>Définition</td> + <td>Une classe est définie avec une définition de classe. On instancie une classe avec des méthodes appelées constructeurs</td> + <td>On définit et on crée un ensemble d’objets avec des fonctions qui sont des constructeurs.</td> + </tr> + <tr> + <td>Création d'un nouvel objet</td> + <td>On crée un seul objet grâce à l’opérateur <code>new</code>.</td> + <td>Même chose que pour les langages de classe.</td> + </tr> + <tr> + <td>Construction de la hiérarchie des objets</td> + <td>On construit une hiérarchie d’objets en utilisant les définitions des classes pour définir des classes-filles à partir de classes existantes.</td> + <td>On construit une hiérarchie d’objets en assignant un prototype à un objet dans le constructeur de cet objet.</td> + </tr> + <tr> + <td>Modèle d'héritage</td> + <td>Les objets héritent des propriétés appartenant à la chaîne des classes de la hiérarchie.</td> + <td>Les objets héritent des propriétés appartenant à la chaîne des prototypes de la hiérarchie.</td> + </tr> + <tr> + <td>Ajout de propriétés</td> + <td>La définition de la classe définit exactement toutes les propriétés de toutes les instances d’une classe. Il est impossible d’ajouter des propriétés dynamiquement pendant l’exécution.</td> + <td>Le constructeur ou le prototype définit un ensemble de propriétés initiales. Il est possible d’ajouter ou de retirer des propriétés dynamiquement, pour certains objets en particuliers ou bien pour l’ensemble des objets.</td> + </tr> + </tbody> +</table> + +<h2 id="L’exemple_de_l’employé">L’exemple de l’employé</h2> + +<p>La suite de ce chapitre expliquera la hiérarchie objet suivante, modélisant un système avec différents employés :</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/3060/figure8.1.png"></p> + +<p><small><strong>Une hiérarchie objet basique</strong></small></p> + +<p>Cet exemple utilisera les objets suivants :</p> + +<ul> + <li><code>Employé</code> qui possède la propriété <code>nom</code> (dont la valeur par défaut est la chaîne de caractères vide) et <code>branche</code> (dont la valeur par défaut est "commun").</li> + <li><code>Manager</code> qui est basé sur <code>Employé</code>. La propriété <code>rapports</code> est ajoutée (la valeur par défaut est un tableau vide, ce sera un tableau rempli d’objets<code> Employés</code>).</li> + <li><code>Travailleur</code> est également basé sur <code>Employé</code>. La propriété <code>project</code> est ajoutée (la valeur par défaut est un tableau vide, ce sera un tableau rempli de chaînes de caractères).</li> + <li><code>Vendeur</code> est basé sur <code>Travailleur. </code>La propriété <code>quota</code> est ajoutée (la valeur par défaut est 100). La propriété <code>branche</code> est surchargée et vaut "ventes", indiquant que tous les vendeurs font partie du même département.</li> + <li><code>Ingénieur</code> est basé sur <code>Travailleur</code>. La propriété <code>moteur</code> est ajoutée (la valeur par défaut est la chaîne vide) et la propriété <code>branche</code> est surchargée avec la valeur "ingénierie".</li> +</ul> + +<h2 id="La_création_de_la_hiérarchie">La création de la hiérarchie</h2> + +<p>Plusieurs fonctions utilisées comme constructeurs peuvent permettre de définir la hiérarchie souhaitée. La façon que vous utiliserez dépendra des fonctionnalités que vous souhaitez avoir dans votre application.</p> + +<p>On utilise ici des définitions très simples (et peu adaptables) permettant de montrer le fonctionnement de l’héritage. Grâce à ces définitions fournies, on ne peut pas définir des valeurs spécifiques pour les propriétés lors de la création de l’objet. Les objets créés reçoivent les valeurs par défaut, les propriétés pourront être changées par la suite.</p> + +<p>Pour une application réelle, on définirait des constructeurs permettant de fixer les valeurs des propriétés lors de la création (voir <a href="#Des_constructeurs_plus_flexibles">Des constructeurs plus flexibles</a>). Ici, ces définitions nous permettent d’observer l’héritage.</p> + +<div class="note"> +<p>Attention, quand on assigne une fonction directement à NomFonction.prototype, cela retire la propriété « constructeur » du prototype original. Ainsi, (new Travailleur).constructor renverra un <code>Employé</code> (et non pas <code>Travailleur</code>). Il faut faire attention à la façon de conserver le constructeur original du prototype. On peut, par exemple, l'assigner à NomFonction.prototype.__proto__. Dans notre exemple, on aurait ainsi, Travailleur.prototype.__proto__ = new Employé; de cette façon : (new Travailleur).constructor renvoie bien « Travailleur ».</p> +</div> + +<p>Les définitions d’<code>Employé</code> suivantes, en Java et JavaScript sont assez semblables. Les seules différences proviennent du typage nécessaire : avec Java, il est nécessaire de préciser le type des propriétés alors que ce n’est pas le cas en JavaScript. En Java, il est également nécessaire de créer une méthode constructeur à part dans la classe.</p> + +<table> + <thead> + <tr> + <th scope="col">JavaScript</th> + <th scope="col">Java</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <pre class="brush: js"> +function Employé () { + this.nom = ""; + this.branche = "commun"; +} +</pre> + </td> + <td> + <pre class="brush: java"> +public class Employé { + public String nom; + public String branche; + public Employé () { + this.nom = ""; + this.branche = "commun"; + } +} +</pre> + </td> + </tr> + </tbody> +</table> + +<p>Les définitions de <code>Manager</code> et <code>Travailleur</code> permettent de voir la différence dans les façons de définir un objet plus loin dans la relation d’héritage. En JavaScript, il faut ajouter une instance prototypique comme valeur de la propriété <code>prototype</code> de la fonction constructeur (autrement dit, on définit la valeur de la propriété <code>prototype</code> de la fonction, en utilisant une instance du prototype utilisé puis en surchargeant <code>prototype.constructor</code>). On peut faire cela à n’importe quel moment après la définition du constructeur. Avec Java, on définit la classe mère au sein de la définition de la classe et il n’est pas possible de définir cette relation en dehors de la définition de la classe.</p> + +<table> + <thead> + <tr> + <th scope="col">JavaScript</th> + <th scope="col">Java</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <pre class="brush: js"> +function Manager () { + this.rapports = []; +} +Manager.prototype = new Employé; + +function Travailleur () { + this.projets = []; +} +Travailleur.prototype = new Employé; +</pre> + </td> + <td> + <pre class="brush: java"> +public class Manager extends Employé { + public Employé[] rapports; + public Manager () { + this.rapports = new Employé[0]; + } +} + +public class Travailleur extends Employé { + public String[] projets; + public Travailleur () { + this.projets = new String[0]; + } +} +</pre> + </td> + </tr> + </tbody> +</table> + +<p>Les définitions d’<code>Ingénieur</code> et de <code>Vendeur</code> permettent de créer des objets qui héritent de <code>Travailleur</code> et donc, implicitement, de <code>Employé</code>. Un objet d’un de ces deux types possède les propriétés de tous les objets présents plus haut dans l’héritage. De plus, dans notre exemple, les définitions de ces types surchargent les valeurs de la propriété <code>branche</code> avec des valeurs spécifiques.</p> + +<table> + <thead> + <tr> + <th scope="col">JavaScript</th> + <th scope="col">Java</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <pre class="brush: js"> +function Vendeur () { + this.branche = "ventes"; + this.quota = 100; +} +Vendeur.prototype = new Travailleur; + +function Ingénieur () { + this.branche = "ingénierie"; + this.moteur = ""; +} +Ingénieur.prototype = new Travailleur; +</pre> + </td> + <td> + <pre class="brush: java"> +public class Vendeur extends Travailleur { + public double quota; + public Vendeur () { + this.branche = "ventes"; + this.quota = 100.0; + } +} + +public class Ingénieur extends Travailleur { + public Ingénieur () { + this.branche = "ingénierie"; + this.moteur = ""; + } +} +</pre> + </td> + </tr> + </tbody> +</table> + +<p>Grâce à ces définitions, on peut créer des instances pour ces objets qui auront les valeurs par défaut pour leurs propriétés. Le schéma suivant montre comment utiliser ces définitions en JavaScript et illustre les propriétés des objets ainsi créés.</p> + +<div class="note"> +<p>Le terme <em><em>instance</em></em> possède un sens particulier, au niveau technique, pour les langages de classes. Pour ces langages, une instance est le résultat de l’instanciation d’une classe en un objet (qui sera un « exemplaire » de cette classe), le concept d’instance est donc fondamentalement différent du concept de classe. En JavaScript, une « instance » ne possède pas de sens technique particulier, ceci est notamment dû au fait qu'il n’y a pas d’opposition entre ces concepts (car il n’y a pas de classe). Cependant, le terme instance peut parfois être utilisé, en JavaScript, pour désigner un objet qui aurait été créé en utilisant un constructeur. De la même façon, les mots <em><em>parent</em>, <em>enfant</em>, <em>ancêtre</em></em>, et <em><em>descendant</em></em> n’ont pas de signification formelle en JavaScript, mais ils peuvent être utilisés pour faire référence aux différents objets appartenant aux différents niveaux de la chaîne de prototypes.</p> +</div> + +<p><img src="https://mdn.mozillademos.org/files/10412/=figure8.3.png"><strong>Créer des objets avec des définitions simples</strong></p> + +<h2 id="Les_propriétés_d’un_objet">Les propriétés d’un objet</h2> + +<p>Dans cette section, nous verrons comment les objets héritent des propriétés d’autres objets de la chaîne de prototypes et de ce qui se passe quand on ajoute une propriété lors de l’exécution.</p> + +<h3 id="L’héritage_de_propriétés">L’héritage de propriétés</h3> + +<p>Imaginons qu’on souhaite créer un objet <code>marc</code> qui est un <code>Travailleur </code>:</p> + +<pre class="brush: js">var marc = new Travailleur; +</pre> + +<p>Lorsque JavaScript rencontre l’opérateur <code>new</code>, un objet générique est créé, ce nouvel objet est passé comme valeur de <code>this</code> à la fonction constructeur de <code>Travailleur</code>. Le constructeur définit ensuite la valeur de la propriété <code>projets</code> puis il définit implicitement la valeur de la propriété interne <code>[[Prototype]]</code> avec la valeur de <code>Travailleur.prototype</code>. (Le nom de cette propriété commence et finit par deux tirets bas.) La propriété <code>__proto__</code> détermine la chaîne de prototypes utilisée pour renvoyer les valeurs des propriétés qu’on pourrait utiliser. Quand ces propriétés sont définies, JavaScript renvoie le nouvel objet, l’instruction d’assignation assigne alors la variable <code>marc</code> à cet objet.</p> + +<p>En utilisant ce procédé, on n’introduit pas de valeurs spécifiques pour les propriétés de <code>marc</code> dont il hérite via la chaîne de prototypes. Si on utilise une valeur d'une de ces propriétés, JavaScript vérifiera tout d’abord si elle appartient à l'objet : si c’est le cas, la valeur est renvoyée. Sinon, JavaScript remonte dans la chaîne de prototypes en utilisant la propriété <code>__proto__</code>. Si un objet de cette chaîne possède une valeur pour cette propriété, la valeur est renvoyée. Si aucune propriété n’est trouvée, JavaScript indique que l’objet ne possède pas cette propriété. Ainsi pour l’objet <code>marc</code> : on aura les propriétés suivantes avec les valeurs respectives :</p> + +<pre class="brush: js">marc.nom = ""; +marc.branche = "commun"; +marc.projets = []; +</pre> + +<p>L’objet <code>marc</code> hérite des valeurs des propriétés <code>nom</code> et <code>branche</code> via le constructeur <code>Employé</code>. Il y a une valeur locale pour la propriété <code>projets</code> grâce au constructeur <code>Travailleur</code>.</p> + +<p>Ces constructeurs ne permettent pas de fournir des valeurs spécifiques, l’information créée est générique pour chaque objet. Les valeurs des propriétés sont celles par défaut, comme pour tous les autres objets créés à partir de <code>Travailleur</code>. On peut, bien sûr, changer les valeurs de ces propriétés pour fournir des valeurs spécifiques :</p> + +<pre class="brush: js">marc.nom = "Marc Dupont"; +marc.branche = "admin"; +marc.projets = ["navigateur"];</pre> + +<h3 id="L’ajout_de_propriétés">L’ajout de propriétés</h3> + +<p>En JavaScript, on peut ajouter des propriétés lors de l’exécution et on peut utiliser des propriétés qui ne seraient pas définies par le constructeur. Afin d’ajouter une propriété à un objet donné, on assigne une valeur de la façon suivante :</p> + +<pre class="brush: js">marc.bonus = 3000; +</pre> + +<p>Désormais, l’objet <code>marc</code> possède la propriété <code>bonus</code>. En revanche, aucun autre <code>Travailleur</code> ne possède cette propriété.</p> + +<p>Si on ajoute une nouvelle propriété à un objet qui est utilisé comme prototype pour un constructeur, alors tous les objets créés à partir de ce constructeur bénéficieront de cette propriété grâce à l’héritage. Ainsi, on peut ajouter une propriété <code>spécialité</code> à tous les employés grâce à l’instruction suivante :</p> + +<pre class="brush: js">Employé.prototype.spécialité = "aucune"; +</pre> + +<p>Dès que l’instruction est exécutée par JavaScript, l’objet <code>marc</code> possèdera aussi la propriété <code>spécialité</code> initialisée avec la valeur <code>"aucune"</code>. Le schéma suivant décrit l’effet de cet ajout puis de la surcharge de cette propriété pour le prototype <code>Ingénieur</code>.</p> + +<p><img alt="" class="internal" src="/@api/deki/files/4422/=figure8.4.png" style="height: 519px; width: 833px;"><br> + <small><strong>Ajouter des propriétés</strong></small></p> + +<h2 id="Des_constructeurs_plus_flexibles">Des constructeurs plus flexibles</h2> + +<p>Les fonctions constructeur utilisées jusqu’à présent ne permettaient pas de définir les valeurs des propriétés à la création d’une instance. De la même façon qu’en Java, il est possible d’utiliser des arguments pour ces fonctions afin d’initialiser les valeurs des propriétés des instances.</p> + +<p><img alt="" class="internal" id="figure8.5" src="/@api/deki/files/4423/=figure8.5.png" style="height: 481px; width: 1012px;"><br> + <small><strong>Définir les propriétés grâce au constructeur</strong></small></p> + +<p>Le tableau suivant montre les définitions de ces objets en JavaScript et en Java.</p> + +<table> + <thead> + <tr> + <th scope="col">JavaScript</th> + <th scope="col">Java</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <pre class="brush: js"> +function Employé (nom, branche) { + this.nom = nom || ""; + this.branche = branche || "commun"; +} +</pre> + </td> + <td> + <pre class="brush: java"> +public class Employé { + public String nom; + public String branche; + public Employé () { + this("", "commun"); + } + public Employé (String nom) { + this(nom, "commun"); + } + public Employé (String nom, String branche) { + this.nom = nom; + this.branche = branche; + } +} +</pre> + </td> + </tr> + <tr> + <td> + <pre class="brush: js"> +function Travailleur (projs) { + this.projets = projs || []; +} +Travailleur.prototype = new Employé; +</pre> + </td> + <td> + <pre class="brush: java"> +public class Travailleur extends Employé { + public String[] projets; + public Travailleur () { + this(new String[0]); + } + public Travailleur (String[] projs) { + this.projets = projs; + } +} + +</pre> + </td> + </tr> + <tr> + <td> + <pre class="brush: js"> + +function Ingénieur (moteur) { + this.branche = "ingénierie"; + this.moteur = moteur || ""; +} +Ingénieur.prototype = new Travailleur; +</pre> + </td> + <td> + <pre class="brush: java"> +public class Ingénieur extends Travailleur { + public String moteur; + public Ingénieur () { + this.branche = "ingénierie"; + this.moteur = ""; + } + public Ingénieur (String moteur) { + this.branche = "ingénierie"; + this.moteur = moteur; + } +} +</pre> + </td> + </tr> + </tbody> +</table> + +<p>Les définitions JavaScript présentées ci-dessus utilisent une instruction qui peut paraître étrange pour avoir des valeurs par défaut :</p> + +<pre class="brush: js">this.nom = nom || ""; +</pre> + +<p>L’opérateur correspondant au OU logique en JavaScript (||) évalue le premier argument. Si cet argument peut être converti en <code>true</code>, alors l’opérateur renverra cet argument. Sinon, il renvoie la valeur du second argument. Ainsi, en utilisant ce code, on teste si la valeur fournie (<code>nom</code>) est utile ou non : si c’est le cas, on l’utilise pour la propriété, sinon on conserve la valeur par défaut (ici la chaîne vide). Cet exemple peut paraître déroutant mais permet d’être plus concis.</p> + +<div class="note"> +<p>Attention, cela peut ne pas fonctionner comme souhaité si le constructeur est appelé avec des arguments qui seront convertis à <code><code>false</code></code> (comme <code>0</code> (zéro) et la chaîne de caractère vide (<code><code>""</code></code>). Si ces valeurs sont utilisées, la valeur par défaut sera prise en compte.</p> +</div> + +<p>Grâce à ces définitions, on peut créer une instance d’un objet en utilisant des valeurs spécifiques pour les propriétés. On peut par exemple utiliser :</p> + +<pre class="brush: js">var jeanne = new Ingénieur("carnot");</pre> + +<p>Les propriétés de l’objet sont donc désormais :</p> + +<pre class="brush: js">jeanne.nom == ""; +jeanne.branche == "ingénierie"; +jeanne.projets == []; +jeanne.moteur == "carnot" +</pre> + +<p>On peut remarquer qu’avec ces définitions, on ne peut pas définir de valeur initiale pour les propriétés provenant de l’héritage, comme <code>nom</code> ici. Si on souhaite définir des valeurs initiales pour ces propriétés, il faut modifier légèrement le constructeur.</p> + +<p>Jusqu’à présent, le constructeur utilisé permettait de créer un objet générique puis de créer des propriétés et de spécifier leurs valeurs pour le nouvel objet. On peut utiliser un constructeur afin de définir des valeurs spécifiques pour les autres propriétés plus hautes dans l’héritage. Pour ce faire, on appelle le constructeur de l’objet plus haut dans l’héritage au sein même du constructeur de l’objet courant.</p> + +<p><img alt="" class="internal" src="/@api/deki/files/4430/=figure8.6.png" style="height: 534px; width: 1063px;"><br> + <small><strong>La définition de propriétés héritées dans un constructeur</strong></small></p> + +<p>Ainsi, le constructeur de <code>Ingénieur</code> sera :</p> + +<pre class="brush: js">function Ingénieur (nom, projets, moteur) { + this.base = Travailleur; + this.base(nom, "ingénierie", projets); + this.moteur = moteur || ""; +} +</pre> + +<p>Si on crée ensuite un objet <code>Ingénieur</code> de cette façon :</p> + +<pre class="brush: js">var jeanne = new Ingénieur("Jeanne Dubois", ["navigateur", "javascript"], "carnot"); +</pre> + +<p>L’exécution du code entraînera les étapes suivantes :</p> + +<ol> + <li>La création d’un objet générique avec l'opérateur <code>new</code> qui assigne la valeur <code>Ingénieur.prototype</code> à la propriété <code>__proto__</code>.</li> + <li>L'opérateur <code>new</code> passe ensuite l’objet au constructeur <code>Ingénieur</code> comme valeur du mot-clé <code>this</code>.</li> + <li>Le constructeur crée une nouvelle propriété, appelée <code>base</code>, pour cet objet. Cette propriété reçoit la valeur du constructeur <code>Travailleur</code>. Ainsi le constructeur <code>Travailleur</code> devient une méthode de l’objet <code>Ingénieur</code>. Le nom de cette propriété <code>base</code> n’est pas spécial, on pourrait utiliser un autre nom pour cette propriété (sous réserve qu’il soit valide).</li> + <li> + <p>Le constructeur appelle la méthode <code>base</code> et lui passe deux arguments qui avaient été passés (<code>"Jeanne Dubois"</code> et <code>["navigateur", "javascript"]</code>), ainsi que la chaîne de caractères <code>"ingénierie"</code>. Le fait d’utiliser <code>"ingénierie"</code> explicitement indique que tous les objets <code>Ingénieur</code> auront la même valeur pour la propriété <code>branche</code> qui aura été héritée. Cela permet également de surcharger la valeur par défaut héritée de <code>Employé</code>.</p> + </li> + <li> + <p><code>base</code> étant une méthode d'<code>Ingénieur</code>, lors de l'appel de cette fonction, le mot clé <code>this</code> aura été lié à l'objet créé en 1. Ainsi, la fonction <code>Travailleur</code> passera les arguments <code>"Jeanne Dubois"</code> et <code>"ingénierie"</code> au constructeur <code>Employé</code>. Une fois que la fonction constructeur <code>Employé</code> a renvoyé un résultat, la fonction <code>Travailleur</code> utilise l'argument restant pour donner la valeur à la propriété <code>projets</code>.</p> + </li> + <li>Une fois que la méthode <code>base</code> a renvoyé un résultat, le constructeur <code>Ingénieur</code> initialise la propriété <code>moteur</code> avec la valeur <code>"carnot"</code>.</li> + <li>Lorsque le constructeur renvoie le résultat, il est assigné à la variable <code>jeanne</code>.</li> +</ol> + +<p>On peut penser qu’un simple appel au constructeur <code>Travailleur</code>, dans le constructeur <code>Ingénieur</code> permette de définir l'héritage pour les objets <code>Ingénieur</code>. Attention, ce n’est pas le cas. Un simple appel au constructeur <code>Travailleur</code> permet de bien définir les valeurs des propriétés spécifiées dans les constructeurs appelés. En revanche, si plus tard on souhaite ajouter des propriétés aux prototypes <code>Employé</code> ou <code>Travailleur</code> : l'objet <code>Ingénieur</code> n’en héritera pas. Si par exemple on a :</p> + +<pre class="brush: js">function Ingénieur (nom, projets, moteur) { + this.base = Travailleur; + this.base(nom, "ingénierie", projets); + this.moteur = moteur || ""; +} +var jeanne = new Ingénieur("Jeanne Dubois", ["navigateur", "javascript"], "carnot"); +Employé.prototype.spécialité = "aucune"; +</pre> + +<p>L'objet <code>jeanne</code> n’héritera pas de la propriété <code>spécialité</code>. Il aurait fallu préciser le prototype pour s'assurer de l'héritage dynamique. Si on a plutôt :</p> + +<pre class="brush: js">function Ingénieur (nom, projets, moteur) { + this.base = Travailleur; + this.base(nom, "ingénierie", projets); + this.moteur = moteur || ""; +} +Ingénieur.prototype= new Travailleur; +var jeanne = new Ingénieur("Jeanne Dubois", ["navigateur", "javascript"], "carnot"); +Employé.prototype.spécialité = "aucune"; +</pre> + +<p>Alors la valeur de la propriété <code>spécialité</code> de <code>jeanne</code> sera "aucune".</p> + +<p>Une autre façon d’utiliser l’héritage dans un constructeur peut être d'utiliser les méthodes <a href="/fr/docs/Web/JavaScript/Reference/Global_Objects/Function/call"><code>call()</code></a> et <a href="/fr/docs/Web/JavaScript/Reference/Global_Objects/Function/apply"><code>apply()</code></a>. Les deux fragments de codes présentés ici sont équivalents :</p> + +<table> + <tbody> + <tr> + <td> + <pre class="brush: js"> +function Ingénieur (nom, projets, moteur) { + this.base = Travailleur; + this.base(nom, "ingénierie", projets); + this.moteur = moteur || ""; +} +</pre> + </td> + <td> + <pre class="brush: js"> +function Ingénieur (nom, projets, moteur) { + Travailleur.call(this, nom, "ingénierie", projets); + this.moteur = moteur || ""; +} +</pre> + </td> + </tr> + </tbody> +</table> + +<p>En utilisant la méthode <code>call()</code> on obtient une syntaxe plus claire car on n’utilise plus la propriété intermédiaire <code>base</code>.</p> + +<h2 id="L’héritage_de_propriétés_les_subtilités">L’héritage de propriétés : les subtilités</h2> + +<p>Les sections précédentes ont permis de décrire le fonctionnement des constructeurs et des prototypes, notamment par rapport aux hiérarchies d’objets et à l’héritage. L’objectif de cette section est de couvrir un peu plus en profondeur certaines facettes de l'héritage qui n’étaient pas détaillées avant.</p> + +<h3 id="Valeurs_locales_et_valeurs_héritées">Valeurs locales et valeurs héritées</h3> + +<p>Quand on accède à la propriété d’un objet, JavaScript effectue les étapes suivantes :</p> + +<ol> + <li>On vérifie si la valeur existe localement : si c'est le cas on renvoie cette valeur.</li> + <li>S'il n’y a pas de valeur locale, on parcourt la chaîne des prototypes grâce à la propriété<code>__proto__</code>.</li> + <li>Si un objet de la chaîne de prototypes possède une valeur pour la propriété recherchée, alors on renvoie cette valeur.</li> + <li>Si aucune propriété correspondante n’est trouvée, alors l’objet ne possède pas cette propriété.</li> +</ol> + +<p>L'issue de cet algorithme peut dépendre de la façon dont on a défini au fur et à mesure les différents objets. Dans l'exemple initial on avait les définitions :</p> + +<pre class="brush: js">function Employé () { + this.nom = ""; + this.branche = "commun"; +} + +function Travailleur () { + this.projets = []; +} +Travailleur.prototype = new Employé; +</pre> + +<p>Si on utilise ces définitions et qu'on définit une instance de <code>Travailleur</code> appelée <code>amy</code> avec l'instruction suivante :</p> + +<pre class="brush: js">var amy = new Travailleur; +</pre> + +<p>Alors l'objet <code>amy</code> possède une propriété locale : <code>projets</code>. Les valeurs des propriétés <code>nom</code> et <code>branche</code> ne sont pas locales, elles sont obtenues grâce à la propriété <code>__proto__</code> de l'objet amy. Ainsi <code>amy</code> possède les propriétés suivantes avec les valeurs respectives :</p> + +<pre class="brush: js">amy.nom == ""; +amy.branche == "commun"; +amy.projets == []; +</pre> + +<p>Si maintenant on change la valeur de la propriété <code>nom</code> pour le prototype associé à <code>Employé</code> :</p> + +<pre class="brush: js">Employé.prototype.nom = "Inconnu" +</pre> + +<p>On pourrait penser que cette nouvelle valeur est propagée pour toutes les instances de <code>Employé</code>. Ce n’est pas le cas.</p> + +<p>En effet, lorsqu’on crée n’importe quelle instance de <code>Employé</code>, cette instance obtient une valeur locale pour la propriété <code>nom</code> (qui est la chaîne de caractères vide). Cela signifie que lorsqu'on utilise le prototype <code>Travailleur</code> dans lequel on crée un nouvel objet <code>Employé</code>, <code>Travailleur.prototype</code> aura une valeur locale pour la propriété <code>nom</code>. Ainsi, quand JavaScript recherche la propriété <code>nom</code> de l'objet <code>amy</code> (qui est une instance de <code>Travailleur</code>), JavaScript trouve la valeur locale de cette propriété au niveau de <code>Travailleur.prototype</code> et ne remonte pas plus loin dans la chaîne : on n’atteint pas <code>Employé.prototype</code>.</p> + +<p>Si on souhaite changer la valeur de la propriété d’un objet pendant l’exécution et que sa valeur soit héritée par tous les descendants de l’objet, on ne peut pas définir cette propriété dans le constructeur de l'objet, il faut l’ajouter au constructeur du prototype associé. Si on change le code précédent par :</p> + +<pre class="brush: js">function Employé () { + this.branche = "commun"; // La propriété this.nom, qui est une variable locale, n'apparaît pas ici +} +Employé.prototype.nom = ""; // Il s'agit d'une simple affectation + +function Travailleur () { + this.projets = []; +} +Travailleur.prototype = new Employé; + +var amy = new Travailleur; + +Employé.prototype.nom = "Inconnnu"; +</pre> + +<p>Alors on aura bien la propriété <code>nom</code> de <code>amy</code> qui deviendra "Inconnu".</p> + +<p>Comme on a pu le voir avec ces exemples, si on souhaite avoir des valeurs par défaut pour certaines propriétés et être capable de les modifier à l'exécution, il est nécessaire de les définir dans les propriétés du prototype du constructeur et non dans le constructeur même.</p> + +<h3 id="Comment_déterminer_les_relations_entre_les_instances">Comment déterminer les relations entre les instances</h3> + +<p>La recherche de propriétés JavaScript permet de rechercher parmi les propriétés de l’objet puis dans la propriété spéciale <code>__proto__</code> et ainsi de suite pour explorer la chaîne des prototypes.</p> + +<p>La propriété spéciale <code>__proto__</code> est défnie à la construction d'un objet et contient la valeur du constructeur de la propriété<code>prototype</code>. Ainsi, l’expression <code>new Toto()</code> crée un nouvel objet avec <code>__proto__ == <code class="moz-txt-verticalline">Toto.prototype</code></code>. Ainsi, tous les changements des propriétés de <code class="moz-txt-verticalline">Toto.prototype</code> sont propagés sur la chaîne des prototypes des objets ayant été créés par <code>new Toto()</code>.</p> + +<p>Chaque objet (sauf <code>Object</code>) possède une propriété <code>__proto__</code>. Chaque fonction possède une propriété <code>prototype</code>. Ainsi, on peut relier les objets par « héritage prototypique ». Pour tester l’héritage, on peut comparer la propriété <code>__proto__</code> d'un objet avec la propriété <code>prototype</code> d'une fonction. JavaScript permet de faire ceci avec l’opérateur <code>instanceof</code> qui teste un objet par rapport à une fonction et qui renvoie true si l’objet hérite de la fonction prototype. Ainsi :</p> + +<pre class="brush: js">var t = new Toto(); +var isTrue = (t instanceof Toto);</pre> + +<p>Pour avoir un exemple plus précis, si on crée un objet <code>Ingénieur</code> comme ceci :</p> + +<pre class="brush: js">var chris = new Engineer("Chris Pigman", ["jsd"], "fiji"); +</pre> + +<p>L’ensemble des instructions suivantes renverra <code>true</code> :</p> + +<pre class="brush: js">chris.__proto__ == Ingénieur.prototype; +chris.__proto__.__proto__ == Travailleur.prototype; +chris.__proto__.__proto__.__proto__ == Employé.prototype; +chris.__proto__.__proto__.__proto__.__proto__ == Object.prototype; +chris.__proto__.__proto__.__proto__.__proto__.__proto__ == null; +</pre> + +<p>On pourrait donc écrire la une fonction <code>instanceOf</code> de la façon suivante :</p> + +<pre class="brush: js">function instanceOf(object, constructor) { + while (object != null) { + if (object == constructor.prototype) + return true; + if (typeof object == 'xml') { + return constructor.prototype == XML.prototype; + } + object = object.__proto__; + } + return false; +} +</pre> + +<div class="note"><strong>Note :</strong> L’implémentation ci-dessus possède un cas particulier pour le type d'objet "xml" : c'est une façon de traiter les objets XML et la façon dont ils sont représentés dans les versions plus récentes de JavaScript. N’hésitez pas à aller consulter la fiche {{bug(634150)}} si vous voulez en savoir plus.</div> + +<p class="note">Ainsi, avec cette fonction, les expressions suivantes seront vérifiées :</p> + +<pre class="brush: js">instanceOf (chris, Ingénieur) +instanceOf (chris, Travailleur) +instanceOf (chris, Employé) +instanceOf (chris, Object) +</pre> + +<p>En revanche, l’expression qui suit est fausse :</p> + +<pre class="brush: js">instanceOf (chris, Vendeur) +</pre> + +<h3 id="Les_informations_globales_dans_les_constructeurs">Les informations globales dans les constructeurs</h3> + +<p>Lorsqu'on crée des constructeurs, on doit prendre des précautions si on souhaite manipuler des informations globales au sein d'un constructeur. Si, par exemple, on souhaite avoir un identifiant unique attribué automatiquement pour chaque nouvel employé, on pourrait utiliser la définition suivante :</p> + +<pre class="brush: js">var idCompteur = 1; + +function Employé (nom, branche) { + this.nom = nom || ""; + this.branche = branche || "commun"; + this.id = idCompteur++; +} +</pre> + +<p>Avec cette définition, si on utilise les instructions suivantes <code>victoria.id</code> sera 1 et <code>henri.id</code> sera 2:</p> + +<pre class="brush: js">var victoria = new Employé("Victoria Rander", "international") +var henri = new Employé("Henri Jelier", "ventes") +</pre> + +<p>De cette façon, on peut penser que cette solution convient. Cependant, <code>idCompteur</code> sera incrémenté à chaque fois qu’un objet <code>Employé</code> sera créé, quelqu’en soit la raison. Si on utilise la hiérarchie établie au cours de ce chapitre, le constructeur <code>Employé</code> sera appelé à chaque fois qu’on définit un prototype. Avec le code suivant par exemple :</p> + +<pre class="brush: js">var idCompteur = 1; + +function Employé (nom, branche) { + this.nom = nom || ""; + this.branche = branche || "commun"; + this.id = idCompteur++; +} + +function Manager (nom, branche, rapports) {...} +Manager.prototype = new Employé; + +function Travailleur (nom, branche, projets) {...} +Travailleur.prototype = new Employé; + +function Ingénieur (nom, projets, moteur) {...} +Ingénieur.prototype = new Travailleur; + +function Vendeur (nom, projets, quota) {...} +Vendeur.prototype = new Travailleur; + +var alex = new Ingénieur("Alex S"); +</pre> + +<p>Si on prend le cas où les définitions utilisent la propriété <code>base</code> et appellent le constructeur à chaque fois au-dessus, alors la propriété <code>alex.id</code> vaudra 3.</p> + +<p>Selon l’application qu’on a de cette information, on peut souhaiter ou non que ce compteur ne soit pas incrémenté avec ces étapes intermédiaires. Si on souhaite utiliser une valeur exacte, on pourra utiliser le constructeur suivant :</p> + +<pre class="brush: js">function Employé (nom, branche) { + this.nom = nom || ""; + this.branche = branche || "commun"; + if (nom) + this.id = idCompteur++; +} +</pre> + +<p>Lorsqu’on crée une instance d’<code>Employé</code> comme prototype, on ne fournit pas d’argument au constructeur. Ainsi, en utilisant ce constructeur, lorsqu’on ne fournit pas d’argument, le constructeur n’incrémente pas le compteur. De cette façon, pour qu’un employé ait un identifiant valide, il faut qu’on lui ait donné un nom. Avec cet exemple, <code>alex.id</code> vaudrait 1.</p> + +<p>Une autre façon de procéder est de créer une copie du prototype d'<code>Employé</code> et d'affecter celle-ci à <code>Travailleur</code> :</p> + +<pre class="brush: js">Travailleur.prototype = Object.create(Employé.prototype); +// plutôt que Travailleur.prototype = new Employé;</pre> + +<h3 id="L’absence_d’héritage_multiple">L’absence d’héritage multiple</h3> + +<p>Certains langages orientés objet permettent d’avoir un héritage multiple. Cela veut dire qu’un objet peut hériter des propriétés et des valeurs d’objets parents qui n’ont aucune relation. JavaScript ne permet pas d’avoir ce type d'héritage.</p> + +<p>L’héritage des valeurs des propriétés s’effectue lors de l’exécution lorsque JavaScript explore la chaîne de prototype. Étant donné qu’un objet possède un seul prototype associé, JavaScript ne peut pas, dynamiquement, effectuer l’héritage sur plus d’une chaîne de prototypes.</p> + +<p>En JavaScript, il est possible d’avoir un constructeur qui fait appel à plusieurs constructeurs. Cela donne en quelque sorte l’illusion d’un héritage multiple. Par exemple :</p> + +<pre class="brush: js">function Passioné (passion) { + this.passion = passion || "plongée"; +} + +function Ingénieur (nom, projets, moteur, passion) { + this.base1 = Travailleur; + this.base1(nom, "ingénierie", projets); + this.base2 = Passioné; + this.base2(passion); + this.moteur = moteur || ""; +} +Ingénieur.prototype = new Travailleur; + +var denis = new Ingénieur("Denis Carle", ["collabra"], "carnot") +</pre> + +<p>Supposons que la définition de <code>Travailleur</code> soit utilisée plus haut dans ce chapitre. Dans ce cas, l’objet<code> dennis</code> aura les propriétés suivantes :</p> + +<pre class="brush: js">denis.nom == "Denis Carle" +denis.branche == "ingénierie" +denis.projets == ["collabra"] +denis.moteur == "carnot" +denis.passion == "plongée" +</pre> + +<p>On voit bien que <code>denis</code> bénéficie de la propriété <code>passion</code> du constructeur <code>Passioné</code>. Cependant, si, plus tard, on ajoute une propriété au prototype du constructeur :</p> + +<pre class="brush: js">Passioné.prototype.équipement = ["masque", "filet", "club", "balles"] +</pre> + +<p>L’objet <code>denis</code> n’héritera pas de cette nouvelle propriété.</p> + +<p>{{PreviousNext("Web/JavaScript/Guide/Utiliser_les_objets", "Web/JavaScript/Guide/Utiliser_les_promesses")}}</p> |