Pourquoi est-il nécessaire de définir le prototype du constructeur?
Dans le la section à propos de l'héritage dans le MDN article Introduction à l'Orienté Objet, Javascript, j'ai remarqué qu'ils définir le prototype.constructeur:
//correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
Ne ce servent un objectif important? Est-il possible de le supprimer?
- Heureux que vous avez demandé ceci: j'ai lu la même documentation hier et était curieux de connaître le raisonnement de définir explicitement le constructeur ainsi.
- J'ai juste eu à le souligner, cette question est désormais lié dans l'article lié!
- rien n'est nécessaire
- Le
subclass.prototype.constructor
sera point àparent_class
si vous n'écrivez passubclass.prototype.constructor = subclass
; C'est, en utilisantsubclass.prototype.constructor()
directement va produire un résultat inattendu.
Vous devez vous connecter pour publier un commentaire.
Il n'est pas toujours nécessaire, mais il a ses utilise. Supposons que nous souhaitions faire une copie de la méthode sur la base
Person
classe. Comme ceci:Maintenant ce qui se passe lorsque nous créons un nouveau
Student
et les copier?La copie n'est pas une instance de
Student
. C'est parce que (sans explicite les vérifications), nous n'aurions aucun moyen pour revenir unStudent
copie à partir de la "base" de la classe. Nous ne pouvons que renvoyer unePerson
. Cependant, si nous avions réinitialiser le constructeur:...alors tout fonctionne comme prévu:
constructor
attribut n'a pas de signification particulière en JS, de sorte que vous pourriez aussi bien l'appelerbananashake
. La seule différence est que le moteur automatiquement initialiseconstructor
surf.prototype
chaque fois que vous déclarez une fonctionf
. Cependant, il peut être écrasé à tout moment.constructor
signifie que t ont une signification spéciale dans JS, presque par définition.constructor
à une "classe" dans le but de modifier le comportement denew
, mais vous devez le faire dans l'autre sens: d'Attribuer uneprototype
à la fonction. Ayant la circulaire de référence par la mise enconstructor
peut être une bonne idée, mais échoue lorsque les deux fonctions de point à la mêmeprototype
.return new this.constructor(this.name);
au lieu dereturn new Person(this.name);
. Depuisthis.constructor
est leStudent
fonction (parce que vous définissez avecStudent.prototype.constructor = Student;
), lecopy
fonction finit par appeler laStudent
fonction. Je ne suis pas sûr de ce que votre intention était avec le//just as bad
commentaire.Person
objet au lieu où nous devions nous attendre à la plus spécifiqueStudent
Student
constructeur a ajouté un argument supplémentaire comme:Student(name, id)
? - Nous alors de remplacer lacopy
fonction, l'appel de laPerson
version de l'intérieur, et puis aussi la copie supplémentaireid
propriété?copy
méthode qui copie les deux propriétés. Je ne vois pas du tout besoin d'appeler le "parent" de la méthode.constructor
a juste autant de problèmes que la possibilité d'appeler directementnew Person()
. Comme @CEGRD dit, l'extension de l'objet de la fonction peut être incompatible. Si, à l'aide deconstructor
comme ceci comme une abréviation peut être justifiée si elle arrive à travailler dans la plupart des cas, car elle réduit la duplication de code. Mais il est utilisé comme un moyen pour les types d'opt-in à un clone de mise en œuvre, non pas comme une chose en soi (autres que JavaScript arrive à le définir, parfois...).student1
comme son[[Prototype]]
et pasStudent.prototype
comme son[[Prototype]]
.Student
fonction, c'estprototype.constructor
estStudent
. Toutefois, lorsqueStudent.prototype = Object.create(Person.prototype);
est appelé un effet secondaire négatif, c'est que leStudent.prototype.constructor
propriété est définie àPerson
. Donc, cela peut être fixe en définissant le prototype.constructeur de retour àStudent
.Oui et non.
Dans l'ES5 et plus tôt, JavaScript lui-même n'a pas utilisé
constructor
pour quoi que ce soit. Il a défini que la valeur par défaut de l'objet sur une fonctionprototype
bien aurait-il et qu'il se réfère à la fonction, et que c'était. Rien d'autre dans le cahier des charges, visée à tous.Qui a changé dans ES2015 (ES6), qui a commencé à l'utiliser en relation avec des hiérarchies d'héritage. Par exemple,
Promesse#puis
utilise leconstructor
propriété de la promesse que vous l'appelez, via SpeciesConstructor) lors de la construction de la nouvelle promesse de revenir. Il est aussi impliqué dans le sous-typage des tableaux (via ArraySpeciesCreate).En dehors de la langue elle-même, parfois, les gens l'utilisent en essayant de construire générique "clone" de fonctions ou, en général, quand ils ont voulu se référer à ce qu'ils croyaient serait le constructeur de l'objet fonction. Mon expérience est que son utilisation est rare, mais parfois les gens ne l'utilisent.
Il est là par défaut, vous avez seulement besoin de le remettre lorsque vous remplacer l'objet d'une fonction
prototype
propriété:Si vous ne le faites pas:
...puis
Student.prototype.constructor
hérite dePerson.prototype
qui (probablement) aconstructor = Person
. Il est donc trompeuse. Et bien sûr, si vous êtes sous-classement quelque chose qui l'utilise (commePromise
ouArray
) et n'utilisant pas declass
1 (qui gère cela pour vous), vous devrez assurez-vous de régler correctement. Donc en gros: C'est une bonne idée.C'est pas grave si rien dans votre code (ou code de bibliothèque que vous utilisez) l'utilise. J'ai toujours veillé à ce qu'il a été correctement câblé.
Bien sûr, avec ES2015 (aka ES6)'s
class
mot-clé, la plupart du temps, nous les avons utilisés, nous n'avons pas plus, parce que c'est géré pour nous lorsque nous ne1 "...si vous êtes sous-classement quelque chose qui l'utilise (comme
Promise
ouArray
) et n'utilisant pas declass
..." — C'est possible de le faire, mais c'est une vraie douleur (et un peu stupide). Vous devez utiliserRéfléchir.construction
.TLDR; Pas super nécessaire, mais sera probablement aider dans le long terme, et il est plus précis de le faire.
NOTE: édité comme ma réponse précédente était de prêter à confusion, écrit et avait quelques erreurs que j'ai raté dans mon désir de répondre. Merci à ceux qui ont relevé certaines erreurs flagrantes.
Fondamentalement, c'est au fil de sous-classer correctement en Javascript. Quand nous partirons, nous avons à faire certaines choses funky pour vous assurer que les prototypes délégation fonctionne correctement, y compris le remplacement d'un
prototype
objet. L'écrasement d'unprototype
objet comprend lesconstructor
, donc, nous avons alors besoin de fixer la référence.Nous allons rapidement passer en revue la façon dont des "classes" dans ES5 travail.
Disons que vous avez une fonction de constructeur et de son prototype:
Lorsque vous appelez le constructeur à instancier, dire
Adam
:La
new
mot-clé appelée avec la "Personne" de coeur va exécuter le constructeur Personne avec quelques lignes de code supplémentaires:Si nous
console.log(adam.species)
, la recherche échouera à laadam
exemple, et regarder les prototypes de la chaîne à ses.prototype
, qui estPerson.prototype
- etPerson.prototype
a un.species
de la propriété, de sorte que la recherche va réussir àPerson.prototype
. Il va alors se connecter'human'
.Ici, le
Person.prototype.constructor
correctement le point dePerson
.Alors maintenant, la partie la plus intéressante, la soi-disant "sous-classement'. Si nous voulons créer un
Student
classe, qui est une sous-classe de laPerson
classe avec quelques modifications supplémentaires, nous allons avoir besoin pour s'assurer que leStudent.prototype.constructor
points à l'Étudiant pour la précision.Il ne fait pas cela par lui-même. Lorsque vous sous-classe, le code ressemble à ceci:
Appel
new Student()
ici serait de retour d'un objet avec toutes les propriétés que nous voulons. Ici, si nous vérifions leseve instanceof Person
, il serait de retourfalse
. Si l'on essaie d'accéder àeve.species
, il serait de retourundefined
.En d'autres termes, nous avons besoin de câbler la délégation de sorte que
eve instanceof Person
renvoie true, et donc que les instances deStudent
délégué correctement àStudent.prototype
, puisPerson.prototype
.MAIS depuis que nous l'appelons avec le
new
mot-clé, n'oubliez pas ce que l'invocation ajoute? Il ferait appelObject.create(Student.prototype)
, qui est la façon dont nous avons mis en place que delegational relation entreStudent
etStudent.prototype
. Notez que pour l'instant,Student.prototype
est vide. Donc à la recherche.species
une instance deStudent
serait un échec qu'il délègue à seulementStudent.prototype
, et la.species
propriété n'existe pas surStudent.prototype
.Quand nous faisons attribuer
Student.prototype
àObject.create(Person.prototype)
,Student.prototype
lui-même, puis les délégués àPerson.prototype
, et la recherche d'eve.species
sera de retourhuman
que nous attendons. Sans doute, nous voulons qu'elle hérite de l'Étudiant.prototype ET par Personne.le prototype. Donc nous avons besoin pour corriger tout cela.Maintenant, la délégation travaille, mais nous sommes d'écraser
Student.prototype
avec un dePerson.prototype
. Donc, si nous appelonsStudent.prototype.constructor
, il ne serait point àPerson
au lieu deStudent
. Ce est pourquoi nous avons besoin de la corriger.Dans l'ES5, notre
constructor
propriété est une référence qui fait référence à une fonction que nous avons écrit avec l'intention d'être une 'constructeur'. Côté de ce que lenew
mot-clé nous donne, le constructeur est le contraire d'un "simple" fonction.Dans l'ES6, la
constructor
est maintenant intégrée dans la façon d'écrire des classes - comme dans, il est fourni en tant que méthode lors de la déclaration d'une classe. C'est simplement du sucre syntaxique, mais il est en accord nous des commodités comme l'accès à unsuper
quand nous permet d'étendre une classe existante. Donc, nous devons écrire le code ci-dessus comme ceci:eve instanceof Student
retournétrue
. Voir stackoverflow.com/questions/35537995/... pour l'explication. Aussi quand vous diteswhich is, at the moment, nothing
de quoi parlez-vous? Chaque fonction a un prototype donc, si je checkStudent.prototype
c'est quelque chose.Object.create(Person.prototype)
, leStudent.prototype
est vide. Donc, si nous journaleve.species
, il ne sera pas déléguer correctement jusqu'à sa super-classe, Personne, et il ne sera pas le journal de'human'
. Sans doute, nous voulons que chaque sous-classe d'hériter de son prototype et aussi son super prototype.which is, at the moment, nothing
, je voulais dire que leStudent.prototype
objet est vide.Student.prototype
àObject.create(Person.prototype)
- qui est, si vous vous souvenez, de la même façon toutes les instances de la Personne sont mis en place pour délégué àPerson.prototype
- la recherche d'une propriété sur une instance deStudent
serait délégué à queStudent.prototype
. Donceve.species
va faillir à sa recherche. Si nous ne les céder,Student.prototype
lui-même, puis les délégués àPerson.prototype
, et la recherche d'eve.species
sera de retourhuman
.instance
la "sous-classe" Constructor, il sera précis." Nope,instanceof
ne pas utiliserconstructor
. "Cependant, si nous regardons de l'étudiant .le prototype.constructeur, il ne serait point à la Personne" Nope, il seraStudent
. Je ne comprends pas le point de cet exemple. L'appel d'une fonction dans un constructeur n'est pas l'héritage. "Dans l'ES6, le constructeur est maintenant une fonction réelle au lieu d'une référence à une fonction" Euh quoi?instanceof
utilise la chaîne de prototype. À la suite de la ligneStudent.prototype = Object.create(Person.prototype)
, nous avons remplacé l'originalStudent.prototype
etStudent.prototype.constructor
seraitPerson
. Je suis actuellement à la fixation de la réponse à la suppression de cette confusion.constructor
est en fait une méthode donnée pour nous (quoique sucre syntaxique) avec des mots-clés commesuper
pour la commodité plutôt que comme un "simple" fonction qui est appelée avecnew
.Je serais en désaccord. Il n'est pas nécessaire de définir le prototype. Prendre l'exacte même code, mais retirer le prototype.constructeur de la ligne. Ne rien changer? Pas de. Maintenant, apportez les modifications suivantes:
et à la fin de l'essai le code...
La couleur est bleu.
Un changement pour le prototype.constructeur, dans mon expérience, ne pas le faire, sauf si vous êtes en train de faire très spécifique, très compliqué les choses qui ne sont probablement pas une bonne pratique de toute façon 🙂
Edit:
Après avoir farfouillé sur le web pour un peu et faire quelques essais, ça ressemble à des personnes définir le constructeur afin qu'il "regarde" comme la chose qui est en cours de construction avec les "nouveaux". Je suppose que je dirais que le problème c'est que javascript est un prototype de la langue - il n'y a pas une telle chose comme l'héritage. Mais la plupart des programmeurs qui viennent d'un milieu de programmation qui pousse l'héritage comme "le chemin". Nous arrivons donc avec toutes sortes de choses à essayer et à faire de ce prototype de la langue "classique" de la langue.. comme l'extension des "classes". Vraiment, dans l'exemple qu'ils ont donné, un nouvel élève est une personne - il n'est pas "étendre" à partir d'un autre étudiant.. l'étudiant est tout au sujet de la personne, et quelle que soit la personne est l'étudiant est ainsi. Étendre l'étudiant, et tout ce que vous avez prolongé est un étudiant au coeur, mais est personnalisé pour répondre à vos besoins.
Crockford est un peu fou et les excès de zèle, mais faire un peu de sérieux de lecture sur certaines des choses qu'il écrit.. il fera de vous regarder ce genre de choses très différemment.
Cela a l'énorme piège que si vous avez écrit
mais alors, si il y avait un Professeur dont le prototype a été également Personne et que vous avez écrit
ensuite, l'Élève constructeur est maintenant Professeur!
Edit:
Vous pouvez éviter cela en veillant à ce que vous aviez mis de l'Élève et l'Enseignant à l'aide de prototypes de nouvelles instances de la classe Personne créée à l'aide de l'Objet.créer, comme dans l'exemple Mozilla.
Student.prototype = Object.create(...)
suppose dans cette question. Cette réponse n'ajoute rien, mais de confusion possible.Object.create(...)
est utilisé dans le MDN article à l'origine de la question, mais pas dans la question elle-même. Je suis sûr que beaucoup de gens ne cliquez pas à travers.Jusqu'à présent, la confusion est toujours là.
Suivant l'exemple d'origine, que vous avez un objet existant
student1
comme:Supposons que vous ne voulez pas savoir comment
student1
est créé, vous voulez juste un autre objet, comme cela, vous pouvez utiliser le constructeur de la propriété destudent1
comme:Ici, il n'obtiendra pas les propriétés de
Student
si le constructeur de la propriété n'est pas définie. Ça va plutôt créer unPerson
objet.Obtenu un bon exemple de code d'pourquoi il est vraiment nécessaire de définir le prototype du constructeur..
createNewCar
méthode est la création d'usines!? Aussi, cela ressemble comme il aurait été utilisé commevar audiFactory = new CarFactory("Audi")
plutôt que d'utiliser l'héritage.this.constructor
en interne, de sorte qu'il n'est pas surprenant qu'il a d'être ensemble. Avez-vous un exemple sans elle?Pas nécessaire pour sauvegarder la fonction de 'classes' ou de la 'Nouvelle' ces jours-ci. Utiliser des objets littéraux.
L'Objet prototype est déjà une "classe". Lorsque vous définissez un objet littéral, c'est déjà une instance de l'Objet prototype. Ceux-ci peuvent également agir comme un autre prototype de l'objet, etc.
C'est intéressant à lire:
C'est nécessaire quand vous avez besoin d'une alternative à
toString
sans monkeypatching:JS:
foo.constructor()
??MODIFIER, en réalité, j'étais mal. Commentant la ligne ne change pas c'est le comportement à tous. (Je l'ai testé)
Oui, c'est nécessaire. Lorsque vous ne
Student.prototype.constructor
devientPerson
. Par conséquent, l'appelStudent()
serait de retour à un objet créé parPerson
. Ensuite, si vous neStudent.prototype.constructor
est réinitialisé àStudent
. Maintenant, lorsque vous appelezStudent()
il exécuteStudent
, qui appelle le constructeur parentParent()
, il renvoie l'correctement objet hérité. Si vous n'avez pas de réinitialisationStudent.prototype.constructor
avant de l'appeler, vous obtiendrez un objet qui n'aurait pas de l'un des biens mis enStudent()
.Donnée simple fonction constructeur:
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 d'obtenir automatiquement une propriété appelée constructeur qui renvoie à la fonction à laquelle il est une propriété.
Selon le constructeur, d'autres propriétés et méthodes pourraient être ajoutés au prototype qui n'est pas une pratique très courante, mais encore il est permis pour les extensions.
Donc simplement répondre: nous avons besoin de vous assurer que la valeur à l'état de prototype.le constructeur est correctement réglé comme il est supposé par la spécification.
Ne nous ont toujours réglé correctement cette valeur? Il permet le débogage et rend la structure interne cohérente rapport aux spécifications. L'on doit certainement lors de notre API est utilisée par les tiers, mais pas vraiment quand le code est finalement exécuté dans le runtime.
Voici un exemple de MDN que j'ai trouvé très utile de comprendre ses usages.
En JavaScript, nous avons
async fonctions
qui renvoie AsyncFunction objet.AsyncFunction
n'est pas un objet global mais on peut les récupérer en utilisantconstructor
de la propriété et de l'utiliser.Il n'est pas nécessaire. C'est juste une des nombreuses choses traditionnelles, OOP champions faire pour essayer d'activer le JavaScript de l'héritage par prototype dans la classique héritage. La seule chose que la suite de
n', c'est que vous avez maintenant une référence de la "constructeur".
Dans Wayne réponse, qui a été marqué comme étant correcte, vous pouvez exactement la même chose que le code suivant ne
avec le code ci-dessous (il suffit de remplacer cette.constructeur avec Personne)
Dieu merci, avec ES6 classique héritage puristes peuvent utiliser la langue maternelle des opérateurs comme la classe, s'étend et super et nous n'avons pas à voir, comme prototype.constructeur de corrections et parent refereces.