N'oubliez pas que de nouveaux objets peuvent être créés avec une fonction constructeur, comme new F()
.
Si F.prototype
est un objet, alors l'opérateur new
l'utilise pour définir [[Prototype]]
pour le nouvel objet.
Veuillez noter:
JavaScript avait un héritage prototypique depuis le début. C'était l'une des caractéristiques essentielles du langage.
Mais autrefois, il n’y avait pas d’accès direct. La seule chose qui fonctionnait de manière fiable était une propriété "prototype"
de la fonction constructeur, décrite dans ce chapitre. Il existe donc de nombreux scripts qui l'utilisent encore.
Veuillez noter que F.prototype
signifie ici une propriété régulière nommée "prototype"
sur F
. Cela ressemble à celui du terme « prototype », mais nous entendons ici en réalité une propriété ordinaire portant ce nom.
Voici l'exemple :
soit animal = { mange : vrai } ; function Lapin(nom) { this.name = nom ; } Lapin.prototype = animal ; let lapin = new Rabbit("Lapin Blanc"); // lapin.__proto__ == animal alerte( lapin.eats ); // vrai
Le réglage Rabbit.prototype = animal
indique littéralement ce qui suit : « Lorsqu'un new Rabbit
est créé, attribuez son [[Prototype]]
à animal
».
Voici l'image résultante :
Sur la photo, "prototype"
est une flèche horizontale, signifiant une propriété régulière, et [[Prototype]]
est vertical, signifiant l'héritage du rabbit
d' animal
.
F.prototype
utilisé uniquement au new F
La propriété F.prototype
n'est utilisée que lorsque new F
est appelé, elle attribue [[Prototype]]
du nouvel objet.
Si, après la création, la propriété F.prototype
change ( F.prototype = <another object>
), alors les nouveaux objets créés par new F
auront un autre objet comme [[Prototype]]
, mais les objets déjà existants conserveront l'ancien.
Chaque fonction possède la propriété "prototype"
même si nous ne la fournissons pas.
Le "prototype"
par défaut est un objet avec le seul constructor
de propriété qui renvoie à la fonction elle-même.
Comme ça:
fonction Lapin() {} /* prototype par défaut Rabbit.prototype = { constructeur : Rabbit } ; */
Nous pouvons le vérifier :
fonction Lapin() {} // par défaut : // Lapin.prototype = { constructeur : Lapin } alert( Lapin.prototype.constructor == Lapin ); // vrai
Naturellement, si on ne fait rien, la propriété constructor
est accessible à tous les lapins via [[Prototype]]
:
fonction Lapin() {} // par défaut : // Lapin.prototype = { constructeur : Lapin } let lapin = new Rabbit(); // hérite de {constructeur : Lapin} alert(rabbit.constructor == Lapin); // vrai (à partir du prototype)
Nous pouvons utiliser la propriété constructor
pour créer un nouvel objet en utilisant le même constructeur que celui existant.
Comme ici :
function Lapin(nom) { this.name = nom ; alerte(nom); } let lapin = new Rabbit("Lapin Blanc"); let lapin2 = new lapin.constructor("Lapin Noir");
C'est pratique lorsque nous avons un objet, que nous ne savons pas quel constructeur a été utilisé pour celui-ci (par exemple, il provient d'une bibliothèque tierce) et que nous devons en créer un autre du même type.
Mais la chose la plus importante à propos "constructor"
est probablement que…
…JavaScript lui-même ne garantit pas la bonne valeur "constructor"
.
Oui, il existe dans le "prototype"
par défaut des fonctions, mais c'est tout. Ce qui se passera plus tard dépend entièrement de nous.
En particulier, si nous remplaçons le prototype par défaut dans son ensemble, il n'y aura pas "constructor"
.
Par exemple:
fonction Lapin() {} Lapin.prototype = { sauts : vrai } ; let lapin = new Rabbit(); alert(rabbit.constructor === Lapin); // FAUX
Ainsi, pour conserver le bon "constructor"
nous pouvons choisir d'ajouter/supprimer des propriétés au "prototype"
par défaut au lieu de l'écraser dans son ensemble :
fonction Lapin() {} // Ne pas écraser totalement Rabbit.prototype // il suffit d'y ajouter Lapin.prototype.jumps = vrai // le Rabbit.prototype.constructor par défaut est conservé
Vous pouvez également recréer manuellement la propriété constructor
:
Lapin.prototype = { sauts : vrai, constructeur : Lapin } ; // maintenant le constructeur est également correct, car nous l'avons ajouté
Dans ce chapitre, nous avons brièvement décrit la manière de définir un [[Prototype]]
pour les objets créés via une fonction constructeur. Plus tard, nous verrons des modèles de programmation plus avancés qui en dépendent.
Tout est assez simple, juste quelques notes pour que les choses soient claires :
La propriété F.prototype
(ne la confondez pas avec [[Prototype]]
) définit [[Prototype]]
de nouveaux objets lorsque new F()
est appelé.
La valeur de F.prototype
doit être soit un objet, soit null
: les autres valeurs ne fonctionneront pas.
La propriété "prototype"
n'a un tel effet spécial que lorsqu'elle est définie sur une fonction constructeur et invoquée avec new
.
Sur les objets normaux, le prototype
n'a rien de spécial :
laissez l'utilisateur = { nom : "Jean", prototype : "Bla-bla" // pas de magie du tout } ;
Par défaut, toutes les fonctions ont F.prototype = { constructor: F }
, nous pouvons donc obtenir le constructeur d'un objet en accédant à sa propriété "constructor"
.
importance : 5
Dans le code ci-dessous, nous créons new Rabbit
, puis essayons de modifier son prototype.
Au départ, nous avons ce code :
fonction Lapin() {} Lapin.prototype = { mange : vrai } ; let lapin = new Rabbit(); alerte( lapin.eats ); // vrai
Nous avons ajouté une chaîne supplémentaire (soulignée). Qu'est-ce que alert
affichera maintenant ?
fonction Lapin() {} Lapin.prototype = { mange : vrai } ; let lapin = new Rabbit(); Lapin.prototype = {}; alerte( lapin.eats ); // ?
…Et si le code est comme ça (une ligne remplacée) ?
fonction Lapin() {} Lapin.prototype = { mange : vrai } ; let lapin = new Rabbit(); Lapin.prototype.eats = false; alerte( lapin.eats ); // ?
Et comme ça (remplacé une ligne) ?
fonction Lapin() {} Lapin.prototype = { mange : vrai } ; let lapin = new Rabbit(); supprimer lapin.eats; alerte( lapin.eats ); // ?
La dernière variante :
fonction Lapin() {} Lapin.prototype = { mange : vrai } ; let lapin = new Rabbit(); supprimer Rabbit.prototype.eats ; alerte( lapin.eats ); // ?
Réponses :
true
.
L'affectation à Rabbit.prototype
configure [[Prototype]]
pour les nouveaux objets, mais elle n'affecte pas ceux existants.
false
.
Les objets sont attribués par référence. L'objet de Rabbit.prototype
n'est pas dupliqué, c'est toujours un objet unique référencé à la fois par Rabbit.prototype
et par le [[Prototype]]
de rabbit
.
Ainsi, lorsque l'on modifie son contenu via une référence, il est visible via l'autre.
true
.
Toutes les opérations delete
sont appliquées directement à l'objet. Ici, delete rabbit.eats
essaie de supprimer la propriété eats
de rabbit
, mais il ne l'a pas. L'opération n'aura donc aucun effet.
undefined
.
La propriété eats
est supprimée du prototype, elle n'existe plus.
importance : 5
Imaginez, nous avons un objet arbitraire obj
, créé par une fonction constructeur – nous ne savons pas lequel, mais nous aimerions créer un nouvel objet en l'utilisant.
Peut-on faire comme ça ?
laissez obj2 = new obj.constructor();
Donnez un exemple de fonction constructeur pour obj
qui permet à un tel code de fonctionner correctement. Et un exemple qui fait que cela fonctionne mal.
Nous pouvons utiliser cette approche si nous sommes sûrs que la propriété "constructor"
a la valeur correcte.
Par exemple, si nous ne touchons pas au "prototype"
par défaut, alors ce code fonctionne à coup sûr :
fonction Utilisateur (nom) { this.name = nom ; } let user = new User('Jean'); let user2 = new user.constructor('Pete'); alerte( utilisateur2.nom ); // Pete (a travaillé !)
Cela a fonctionné, car User.prototype.constructor == User
.
…Mais si quelqu'un, pour ainsi dire, écrase User.prototype
et oublie de recréer constructor
pour référencer User
, alors cela échouera.
Par exemple:
fonction Utilisateur (nom) { this.name = nom ; } Utilisateur.prototype = {} ; // (*) let user = new User('John'); let user2 = new user.constructor('Pete'); alerte( utilisateur2.nom ); // non défini
Pourquoi user2.name
n'est undefined
?
Voici comment fonctionne new user.constructor('Pete')
:
Tout d’abord, il recherche constructor
dans user
. Rien.
Ensuite, il suit la chaîne des prototypes. Le prototype de user
est User.prototype
, et il n'a pas non plus constructor
(car nous avons « oublié » de le configurer correctement !).
En remontant plus loin dans la chaîne, User.prototype
est un objet simple, son prototype est le Object.prototype
intégré.
Enfin, pour le Object.prototype
intégré, il existe un Object.prototype.constructor == Object
intégré. Il est donc utilisé.
Enfin, à la fin, nous avons let user2 = new Object('Pete')
.
Ce n'est probablement pas ce que nous voulons. Nous aimerions créer new User
, pas new Object
. C'est le résultat du constructor
manquant.
(Juste au cas où vous seriez curieux, l'appel new Object(...)
convertit son argument en objet. C'est une chose théorique, en pratique personne n'appelle new Object
avec une valeur, et généralement nous n'utilisons pas new Object
pour fabriquer des objets).