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.prototype
est 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. prototype
et 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 undefined
pour la propriété.
Vous pouvez également vous demander pourquoi JavaScript crée une propriété appelée prototype
pour 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. a1
est une variable ordinaire à laquelle un nouvel objet vide a été affecté.
Le fait que vous ayez utilisé l'opérateur new
avant un appel de fonction a A()
fait ADDITIONNEL en arrière-plan. Le new
mot-clé a créé un nouvel objet qui fait maintenant référence a1
et 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 a1
objet 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' new
opérateur, c'est qu'il définit cette __proto__
propriété pour qu'elle pointe vers la prototype
propriété de la fonction . Relisez ça. C'est fondamentalement ceci:
a1.__proto__ = A.prototype;
Nous avons dit que ce A.prototype
n'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, this
est changé en a1
et vous obtenez ceci:
a1.hey = function() { alert('from A') }
Je ne couvrirai pas les raisons du this
changement, a1
mais 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.prototype
points vers (un autre objet vide {})
La fonction A()
est en cours d'exécution avec this
dé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 this
change 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: a2
sera un nouvel objet vide, étape 2: sa __proto__
propriété pointera vers la même chose que A.prototype
vers et surtout, étape 3: la fonction A()
est à nouveau exécutée, ce qui signifie que la propriété contenant une fonction a2
sera obtenue hey
. a1
et a2
ont deux propriétés SEPARATE nommées hey
qui 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 yoMan
proprié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.prototype
pointe 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 a1
ne contient pas hey
et 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 a1
et a2
ayant une séparée hey
proprié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.prototype
propriété comme deux choses différentes.