Prenez ces 2 exemples:
var A = function() { this.hey = function() { alert('from A') } };
contre.
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
La plupart des gens ici (en particulier les réponses les mieux notées) ont essayé d'expliquer en quoi ils diffèrent sans expliquer POURQUOI. Je pense que c'est faux et si vous comprenez d'abord les fondamentaux, la différence deviendra évidente. Essayons d'abord d'expliquer les fondamentaux ...
a) Une fonction est un objet en JavaScript. CHAQUE objet en JavaScript obtient une propriété interne (ce qui signifie que vous ne pouvez pas y accéder comme les autres propriétés, sauf peut-être dans les navigateurs comme Chrome), souvent appelé __proto__(vous pouvez réellement taper anyObject.__proto__Chrome pour voir ce qu'il fait référence. C'est juste que , une propriété, rien de plus. Une propriété en JavaScript = une variable à l'intérieur d'un objet, rien de plus. Que font les variables? Elles pointent vers des choses.
Alors, à quoi __proto__pointe cette propriété? Eh bien, généralement un autre objet (nous expliquerons pourquoi plus tard). La seule façon de forcer JavaScript pour que la __proto__propriété ne pointe PAS vers un autre objet est d'utiliser var newObj = Object.create(null). Même si vous faites cela, la __proto__propriété existe TOUJOURS en tant que propriété de l'objet, juste elle ne pointe pas vers un autre objet, elle pointe vers null.
Voici où la plupart des gens se perdent:
Lorsque vous créez une nouvelle fonction en JavaScript (qui est également un objet, rappelez-vous?), Au moment où elle est définie, JavaScript crée automatiquement une nouvelle propriété sur cette fonction appelée prototype. Essayez-le:
var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined
A.prototypeest TOTALEMENT DIFFÉRENT de la __proto__propriété. Dans notre exemple, «A» a maintenant DEUX propriétés appelées «prototype» et __proto__. C'est une grande confusion pour les gens. prototypeet les __proto__propriétés ne sont aucunement liées, ce sont des choses distinctes pointant vers des valeurs distinctes.
Vous vous demandez peut-être: pourquoi JavaScript a-t-il une __proto__propriété créée sur chaque objet? Eh bien, un mot: délégation . Lorsque vous appelez une propriété sur un objet et que l'objet ne l'a pas, JavaScript recherche l'objet référencé par __proto__pour voir s'il l'a peut-être. S'il ne l'a pas, il regarde la __proto__propriété de cet objet et ainsi de suite ... jusqu'à la fin de la chaîne. D'où le nom de chaîne prototype . Bien sûr, si __proto__ne pointe pas vers un objet et pointe vers la place null, bonne chance, JavaScript s'en rend compte et vous reviendra undefinedpour la propriété.
Vous pouvez également vous demander pourquoi JavaScript crée une propriété appelée prototypepour une fonction lorsque vous définissez la fonction? Parce qu'il essaie de vous tromper, oui vous tromper qu'il fonctionne comme des langages basés sur les classes.
Continuons avec notre exemple et créons un "objet" à partir de A:
var a1 = new A();
Il se passe quelque chose en arrière-plan lorsque cette chose s'est produite. a1est une variable ordinaire à laquelle un nouvel objet vide a été affecté.
Le fait que vous ayez utilisé l'opérateur newavant un appel de fonction a A()fait ADDITIONNEL en arrière-plan. Le newmot-clé a créé un nouvel objet qui fait maintenant référence a1et cet objet est vide. Voici ce qui se passe en plus:
Nous avons dit que sur chaque définition de fonction, une nouvelle propriété créée appelée prototype(à laquelle vous pouvez y accéder, contrairement à la __proto__propriété) créée? Eh bien, cette propriété est utilisée maintenant.
Nous sommes donc maintenant au point où nous avons un a1objet vide fraîchement cuit . Nous avons dit que tous les objets en JavaScript ont une __proto__propriété interne qui pointe vers quelque chose (l'a a1également), que ce soit nul ou un autre objet. Ce que fait l' newopérateur, c'est qu'il définit cette __proto__propriété pour qu'elle pointe vers la prototypepropriété de la fonction . Relisez ça. C'est fondamentalement ceci:
a1.__proto__ = A.prototype;
Nous avons dit que ce A.prototypen'est rien de plus qu'un objet vide (sauf si nous le changeons en quelque chose d'autre avant de le définir a1). Donc, maintenant, a1.__proto__pointe essentiellement vers la même chose A.prototype, qui est cet objet vide. Ils pointent tous les deux vers le même objet qui a été créé lorsque cette ligne s'est produite:
A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}
Maintenant, il se passe autre chose lorsque l' var a1 = new A()instruction est traitée. Fondamentalement, A()est exécuté et si A est quelque chose comme ceci:
var A = function() { this.hey = function() { alert('from A') } };
Toutes ces choses à l'intérieur function() { }vont s'exécuter. Lorsque vous atteignez la this.hey..ligne, thisest changé en a1et vous obtenez ceci:
a1.hey = function() { alert('from A') }
Je ne couvrirai pas les raisons du thischangement, a1mais c'est une excellente réponse pour en savoir plus.
Donc, pour résumer, lorsque vous le faites var a1 = new A(), trois choses se produisent en arrière-plan:
- Un tout nouvel objet vide est créé et affecté à
a1.a1 = {}
a1.__proto__la propriété est assignée pour pointer à la même chose que les A.prototypepoints vers (un autre objet vide {})
La fonction A()est en cours d'exécution avec thisdéfini sur le nouvel objet vide créé à l'étape 1 (lisez la réponse que j'ai référencée ci-dessus pour savoir pourquoi thischange a1)
Maintenant, essayons de créer un autre objet:
var a2 = new A();
Les étapes 1, 2, 3 se répéteront. Avez-vous remarqué quelque chose? Le mot clé est répéter. Étape 1: a2sera un nouvel objet vide, étape 2: sa __proto__propriété pointera vers la même chose que A.prototypevers et surtout, étape 3: la fonction A()est à nouveau exécutée, ce qui signifie que la propriété contenant une fonction a2sera obtenue hey. a1et a2ont deux propriétés SEPARATE nommées heyqui pointent vers 2 fonctions SEPARATE! Nous avons maintenant des fonctions en double dans les mêmes deux objets différents faisant la même chose, oups ... Vous pouvez imaginer les implications en mémoire de cela si nous avons 1000 objets créés avec new A, après toutes les déclarations de fonctions prennent plus de mémoire que quelque chose comme le nombre 2. Donc comment éviter cela?
Rappelez-vous pourquoi la __proto__propriété existe sur chaque objet? Donc, si vous récupérez la yoManpropriété sur a1(qui n'existe pas), sa __proto__propriété sera consultée, qui si c'est un objet (et dans la plupart des cas c'est le cas), elle vérifiera si elle contient yoMan, et si ce n'est pas le cas, il consultera cet objet, __proto__etc. S'il le fait, il prendra cette valeur de propriété et vous l'affichera.
Donc, quelqu'un a décidé d'utiliser ce fait + le fait que lorsque vous créez a1, sa __proto__propriété pointe vers le même objet (vide) A.prototypepointe vers et faites ceci:
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
Cool! Maintenant, lorsque vous créez a1, il passe à nouveau par toutes les 3 étapes ci-dessus, et à l'étape 3, il ne fait rien, car il function A()n'a rien à exécuter. Et si nous le faisons:
a1.hey
Il verra qu'il a1ne contient pas heyet il vérifiera son __proto__objet propriété pour voir s'il l'a, ce qui est le cas.
Avec cette approche, nous éliminons la partie de l'étape 3 où les fonctions sont dupliquées à chaque nouvelle création d'objet. Au lieu de a1et a2ayant une séparée heypropriété, maintenant aucun d'entre eux a. Je suppose que vous vous êtes déjà rendu compte. C'est la bonne chose ... si vous comprenez __proto__et Function.prototype, des questions comme celles-ci seront assez évidentes.
REMARQUE: certaines personnes ont tendance à ne pas appeler la propriété Prototype interne car __proto__, j'ai utilisé ce nom dans la publication pour le distinguer clairement de la Functional.prototypepropriété comme deux choses différentes.