Dans la section sur l'héritage dans l'article MDN Introduction to Object Oriented Javascript , j'ai remarqué qu'ils définissaient le prototype.constructor:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
Cela sert-il un objectif important? Peut-on l'omettre?
javascript
oop
inheritance
trinth
la source
la source
subclass.prototype.constructor
pointera versparent_class
si vous n'écrivez passubclass.prototype.constructor = subclass
; Autrement dit, l'utilisationsubclass.prototype.constructor()
directe produira un résultat inattendu.Réponses:
Ce n'est pas toujours nécessaire, mais il a ses utilisations. Supposons que nous voulions faire une méthode de copie sur la
Person
classe de base . Comme ça:Maintenant, que se passe-t-il lorsque nous créons un nouveau
Student
et le copions?La copie n'est pas une instance de
Student
. En effet (sans contrôles explicites), nous n'aurions aucun moyen de renvoyer uneStudent
copie de la classe "base". Nous ne pouvons renvoyer qu'unPerson
. Cependant, si nous avions réinitialisé le constructeur:... alors tout fonctionne comme prévu:
la source
constructor
attribut n'a pas de signification particulière dans JS, vous pouvez donc aussi bien l'appelerbananashake
. La seule différence est que le moteur initialise automatiquementconstructor
surf.prototype
chaque fois que vous déclarez une fonctionf
. Cependant, il peut être remplacé à tout moment.constructor
signifie qu'il n'ont une signification particulière dans JS, à peu près par définition.return new this.constructor(this.name);
place dereturn new Person(this.name);
. Puisquethis.constructor
c'est laStudent
fonction (parce que vous la définissez avecStudent.prototype.constructor = Student;
), lacopy
fonction finit par appeler laStudent
fonction. Je ne sais pas quelle était votre intention avec ce//just as bad
commentaire.Student
constructeur avait ajouté un argument supplémentaire commeStudent(name, id)
:? Devons-nous alors remplacer lacopy
fonction, appeler laPerson
version de l'intérieur, puis copier également laid
propriété supplémentaire ?Oui et non.
Dans ES5 et versions antérieures, JavaScript lui-même n'utilisait
constructor
rien. Il a défini que l'objet par défaut sur laprototype
propriété d' une fonction l' aurait et qu'il ferait référence à la fonction, et c'était tout . Rien d'autre dans la spécification n'y fait référence du tout.Cela a changé dans ES2015 (ES6), qui a commencé à l'utiliser en relation avec les hiérarchies d'héritage. Par exemple,
Promise#then
utilise laconstructor
propriété de la promesse à laquelle vous l'appelez (via SpeciesConstructor ) lors de la construction de la nouvelle promesse de retour. Il est également impliqué dans les tableaux de sous-typage (via ArraySpeciesCreate ).En dehors du langage lui-même, les gens l'utilisaient parfois lorsqu'ils essayaient de créer des fonctions génériques de «clone» ou tout simplement lorsqu'ils voulaient se référer à ce qu'ils pensaient être la fonction constructeur de l'objet. D'après mon expérience, son utilisation est rare, mais parfois les gens l'utilisent.
Il est là par défaut, il vous suffit de le remettre lorsque vous remplacez l'objet sur la
prototype
propriété d' une fonction :Si vous ne le faites pas:
...
Student.prototype.constructor
hérite alors dePerson.prototype
ce qui (vraisemblablement) aconstructor = Person
. C'est donc trompeur. Et bien sûr, si vous sous-classifiez quelque chose qui l'utilise (commePromise
ouArray
) et que vous n'utilisez pasclass
¹ (qui gère cela pour vous), vous voudrez vous assurer de le définir correctement. Donc en gros: c'est une bonne idée.Ce n'est pas grave si rien dans votre code (ou le code de bibliothèque que vous utilisez) ne l'utilise. J'ai toujours veillé à ce qu'il soit correctement câblé.
Bien sûr, avec le
class
mot clé de ES2015 (aka ES6) , la plupart du temps nous l'aurions utilisé, nous n'avons plus à le faire, car il est géré pour nous lorsque nous le faisons¹ "... si vous sous-classifiez quelque chose qui l'utilise (comme
Promise
ouArray
) et que vous n'utilisez pasclass
..." - C'est possible de le faire, mais c'est vraiment une douleur (et un peu idiot). Vous devez utiliserReflect.construct
.la source
TLDR; Pas super nécessaire, mais aidera probablement à long terme, et il est plus précis de le faire.
REMARQUE: Beaucoup de choses ont été modifiées car ma réponse précédente a été écrite de manière confuse et contient des erreurs que j'ai manquées dans ma précipitation à répondre. Merci à ceux qui ont signalé des erreurs flagrantes.
Fondamentalement, il s'agit de câbler correctement le sous-classement en Javascript. Lorsque nous sous-classons, nous devons faire des choses amusantes pour nous assurer que la délégation prototypique fonctionne correctement, notamment en écrasant un
prototype
objet. Le remplacement d'unprototype
objet inclut leconstructor
, nous devons donc corriger la référence.Voyons rapidement comment fonctionnent les «classes» dans ES5.
Disons que vous avez une fonction constructeur et son prototype:
Lorsque vous appelez le constructeur pour instancier, dites
Adam
:Le
new
mot-clé invoqué avec 'Person' exécutera le constructeur Person avec quelques lignes de code supplémentaires:Si nous
console.log(adam.species)
, la recherche échouera à l'adam
instance et recherchera la chaîne prototypique vers la sienne.prototype
, qui estPerson.prototype
- etPerson.prototype
possède une.species
propriété, la recherche réussira doncPerson.prototype
. Il se connectera ensuite'human'
.Ici, le
Person.prototype.constructor
pointera correctementPerson
.Alors maintenant, la partie intéressante, le soi-disant «sous-classement». Si nous voulons créer une
Student
classe, c'est-à-dire une sous-classe de laPerson
classe avec quelques modifications supplémentaires, nous devons nous assurer que lesStudent.prototype.constructor
points pointent vers Student pour plus de précision.Il ne le fait pas tout seul. Lorsque vous sous-classe, le code ressemble à ceci:
Appeler
new Student()
ici retournerait un objet avec toutes les propriétés que nous voulons. Ici, si nous vérifionseve instanceof Person
, cela reviendraitfalse
. Si nous essayons d'accédereve.species
, il reviendraitundefined
.En d'autres termes, nous devons câbler la délégation afin que
eve instanceof Person
renvoie true et que les instances deStudent
délégué soient correctement envoyées àStudent.prototype
, puisPerson.prototype
.MAIS puisque nous l'appelons avec le
new
mot - clé, rappelez-vous ce que cette invocation ajoute? Cela s'appelleraitObject.create(Student.prototype)
, c'est ainsi que nous établissons cette relation de délégation entreStudent
etStudent.prototype
. Notez qu'en ce moment,Student.prototype
est vide. Ainsi, la recherche d'.species
une instance deStudent
échouerait car elle délègue uniquementStudent.prototype
et la.species
propriété n'existe pasStudent.prototype
.Lorsque nous nous attribuons
Student.prototype
àObject.create(Person.prototype)
,Student.prototype
lui - même , les délégués sontPerson.prototype
renvoyés et la rechercheeve.species
revienthuman
comme prévu. Vraisemblablement, nous voudrions qu'il hérite de Student.prototype ET Person.prototype. Nous devons donc régler tout cela.Maintenant, la délégation fonctionne, mais nous remplaçons
Student.prototype
par un dePerson.prototype
. Donc, si nous appelonsStudent.prototype.constructor
, cela indiquerait auPerson
lieu deStudent
. C'est pourquoi nous devons y remédier.Dans ES5, notre
constructor
propriété est une référence qui fait référence à une fonction que nous avons écrite avec l'intention d'être un «constructeur». Mis à part ce que lenew
mot - clé nous donne, le constructeur est par ailleurs une fonction «simple».Dans ES6, le
constructor
est désormais intégré dans la façon dont nous écrivons les classes - comme dans, il est fourni comme méthode lorsque nous déclarons une classe. Il s'agit simplement de sucre syntaxique, mais il nous offre certaines commodités comme l'accès à unsuper
lorsque nous étendons une classe existante. Nous écririons donc le code ci-dessus comme ceci:la source
eve instanceof Student
retournétrue
. Voir stackoverflow.com/questions/35537995/… pour des explications. Aussi quand vous dites àwhich is, at the moment, nothing
quoi faites-vous référence? Chaque fonction a un prototype donc si je vérifieStudent.prototype
c'est quelque chose.Object.create(Person.prototype)
, leStudent.prototype
est vide. Donc, si nous nous connectonseve.species
, il ne déléguera pas correctement jusqu'à sa superclasse, Personne, et il ne se connectera pas'human'
. Vraisemblablement, nous voulons que chaque sous-classe hérite de son prototype et aussi du prototype de son super.which is, at the moment, nothing
je voulais dire que l'Student.prototype
objet est vide.Student.prototype
àObject.create(Person.prototype)
- qui est, si vous vous souvenez, de la même manière que toutes les instances de Person sont configurées pour déléguer àPerson.prototype
- la recherche d'une propriété sur une instance deStudent
serait déléguée uniquementStudent.prototype
. Alorseve.species
sa recherche échouera. Si nous l'attribuons,Student.prototype
lui - même se délègue alorsPerson.prototype
, et la rechercheeve.species
revienthuman
.instance
le constructeur de la" sous-classe ", elle soit exacte." Non,instanceof
n'utilise pasconstructor
. "Cependant, si nous recherchons le .prototype.constructor de l'étudiant, il pointerait toujours vers la personne" Non, ce seraStudent
. Je ne comprends pas l'intérêt de cet exemple. L'appel d'une fonction dans un constructeur n'est pas un héritage. "Dans ES6, le constructeur est maintenant une fonction réelle au lieu d'une référence à une fonction" Euh quoi?Je ne suis pas d'accord. Il n'est pas nécessaire de définir le prototype. Prenez exactement le même code mais supprimez la ligne prototype.constructor. Est-ce que quelque chose change? Maintenant, apportez les modifications suivantes:
et à la fin du code de test ...
La couleur sera bleue.
Un changement dans le prototype.constructor, d'après mon expérience, ne fait pas grand-chose sauf si vous faites des choses très spécifiques et très compliquées qui ne sont probablement pas de bonnes pratiques de toute façon :)
Edit: Après avoir fouillé un peu sur le Web et avoir fait quelques expérimentations, il semble que les gens définissent le constructeur de sorte qu'il «ressemble» à la chose en cours de construction avec «nouveau». Je suppose que je dirais que le problème avec cela est que javascript est un langage prototype - il n'y a pas d'héritage. Mais la plupart des programmeurs viennent d'un contexte de programmation qui fait de l'héritage «le chemin». Nous proposons donc toutes sortes de choses pour essayer de faire de ce langage prototypique un langage «classique», comme l'extension des «classes». Vraiment, dans l'exemple qu'ils ont donné, un nouvel élève est une personne - ce n'est pas une «extension» à partir d'un autre élève. Prolongez l'étudiant, et tout ce que vous '
Crockford est un peu fou et trop zélé, mais faites une lecture sérieuse sur certaines des choses qu'il a écrites .. cela vous fera regarder les choses très différemment.
la source
Cela a l'énorme écueil que si vous avez écrit
mais alors s'il y avait un enseignant dont le prototype était aussi personne et vous avez écrit
alors le constructeur étudiant est maintenant professeur!
Edit: Vous pouvez éviter cela en vous assurant que vous avez défini les prototypes Student et Teacher à l'aide de nouvelles instances de la classe Person créées à l'aide d'Object.create, comme dans l'exemple Mozilla.
la source
Student.prototype = Object.create(...)
est supposé dans cette question. Cette réponse n'ajoute rien d'autre qu'une confusion possible.Object.create(...)
est utilisé dans l'article MDN qui a engendré la question, mais pas dans la question elle-même. Je suis sûr que beaucoup de gens ne cliquent pas.Jusqu'à présent, la confusion est toujours là.
En suivant l'exemple d'origine, comme vous avez un objet existant en
student1
tant que:Supposons que vous ne vouliez pas savoir comment
student1
est créé, vous voulez juste un autre objet comme celui-ci, vous pouvez utiliser la propriété constructeur destudent1
like:Ici, il ne sera pas possible d'obtenir les propriétés
Student
si la propriété constructeur n'est pas définie. Il créera plutôt unPerson
objet.la source
Vous avez un bel exemple de code expliquant pourquoi il est vraiment nécessaire de définir le constructeur du prototype.
la source
createNewCar
méthode consiste à créer des usines!? Cela semble également avoir dû être utilisévar audiFactory = new CarFactory("Audi")
plutôt que d'utiliser l'héritage.this.constructor
interne, il n'est donc pas surprenant qu'il doive être défini. Avez-vous un exemple sans cela?Plus besoin de «classes» de fonction sucrées ou d'utiliser «Nouveau» de nos jours. Utilisez des littéraux d'objet.
Le prototype Object est déjà une «classe». Lorsque vous définissez un objet littéral, il s'agit déjà d'une instance de l'objet prototype. Ceux-ci peuvent également agir comme prototype d'un autre objet, etc.
Cela vaut la peine d'être lu :
la source
Il est nécessaire lorsque vous avez besoin d'une alternative à
toString
sans monkeypatching:la source
foo.constructor()
??EDIT, j'avais en fait tort. Commenter la sortie ne change pas du tout son comportement. (Je l'ai testé)
Oui il faut. Quand tu fais
Student.prototype.constructor
devientPerson
. Par conséquent, l'appelStudent()
retournerait un objet créé parPerson
. Si vous le faitesStudent.prototype.constructor
est réinitialiséStudent
. Désormais, lorsque vous l'appelez,Student()
il s'exécuteStudent
, ce qui appelle le constructeur parentParent()
, il renvoie l'objet correctement hérité. Si vous n'avez pas réinitialiséStudent.prototype.constructor
avant de l'appeler, vous obtiendrez un objet qui n'aurait aucune des propriétés définiesStudent()
.la source
Étant donné la fonction de constructeur simple:
Par défaut (à partir de la spécification https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ), tous les prototypes obtiennent automatiquement une propriété appelée constructeur qui renvoie à la fonction sur dont c'est une propriété. Selon le constructeur, d'autres propriétés et méthodes peuvent être ajoutées au prototype, ce qui n'est pas une pratique très courante, mais elle est toujours autorisée pour les extensions.
Donc, répondez simplement: nous devons nous assurer que la valeur dans prototype.constructor est correctement définie comme elle est supposée par la spécification.
Faut-il toujours régler correctement cette valeur? Il aide au débogage et rend la structure interne cohérente par rapport aux spécifications. Nous devrions certainement quand notre API est utilisée par des tiers, mais pas vraiment quand le code est finalement exécuté dans le runtime.
la source
Voici un exemple de MDN que j'ai trouvé très utile pour comprendre ses utilisations.
En JavaScript, nous avons
async functions
qui renvoie l' objet AsyncFunction .AsyncFunction
n'est pas un objet global mais on peut le récupérer en utilisant laconstructor
propriété et l'utiliser.la source
Ce n'est pas nécessaire. C'est juste l'une des nombreuses choses traditionnelles que font les champions de la POO pour essayer de transformer l'héritage prototypique de JavaScript en héritage classique. La seule chose que ce qui suit
fait, c'est que vous avez maintenant une référence du "constructeur" actuel.
Dans la réponse de Wayne, qui a été marquée comme correcte, vous pourriez exactement la même chose que le code suivant
avec le code ci-dessous (remplacez simplement this.constructor par Person)
Dieu merci, avec ES6, les puristes de l'héritage classique peuvent utiliser les opérateurs natifs du langage comme classe, étend et super et nous n'avons pas à voir comme les corrections prototype.constructor et les références parentales.
la source