Quelle est la différence entre
var A = function () {
this.x = function () {
//do something
};
};
et
var A = function () { };
A.prototype.x = function () {
//do something
};
javascript
prototype
this
sw234
la source
la source
a1.x !== a2.x
:; sur prototype:a1.x === a2.x
Réponses:
Les exemples ont des résultats très différents.
Avant d'examiner les différences, il convient de noter ce qui suit:
[[Prototype]]
propriété privée de l'instance .myObj.method()
Voici donc les extraits en question:
Dans ce cas, une variable
A
est affectée à une variable qui fait référence à une fonction. Lorsque cette fonction est appelée à l' aideA()
, la fonction est ce n'est pas définie par l'appel si la valeur par défaut l'objet global et l'expressionthis.x
est efficacewindow.x
. Le résultat est qu'une référence à l'expression de fonction sur le côté droit est affectée àwindow.x
.Dans le cas de:
quelque chose de très différent se produit. Dans la première ligne, la variable
A
se voit attribuer une référence à une fonction. En JavaScript, tous les objets fonctions ont une propriété prototype par défaut, il n'y a donc pas de code séparé pour créer un objet A.prototype .Dans la deuxième ligne, A.prototype.x se voit attribuer une référence à une fonction. Cela créera une propriété x si elle n'existe pas ou assignera une nouvelle valeur si elle existe. Donc, la différence avec le premier exemple dans lequel la propriété x de l' objet est impliquée dans l'expression.
Un autre exemple est ci-dessous. Il est similaire au premier (et peut-être ce que vous vouliez demander):
Dans cet exemple, l'
new
opérateur a été ajouté avant l'expression de fonction afin que la fonction soit appelée en tant que constructeur. Lorsqu'elle est appelée avecnew
, la fonction this est définie pour référencer un nouvel objet dont la[[Prototype]]
propriété privée est définie pour référencer le prototype public du constructeur . Ainsi, dans l'instruction d'affectation, lax
propriété sera créée sur ce nouvel objet. Lorsqu'elle est appelée en tant que constructeur, une fonction renvoie son objet this par défaut, il n'est donc pas nécessaire d'avoir unereturn this;
instruction distincte .Pour vérifier que A a une propriété x :
Il s'agit d'une utilisation peu courante de new car la seule façon de référencer le constructeur est via A.constructor . Il serait beaucoup plus courant de faire:
Une autre façon d'obtenir un résultat similaire consiste à utiliser une expression de fonction immédiatement invoquée:
Dans ce cas,
A
affectez la valeur de retour de l'appel de la fonction sur le côté droit. Là encore, puisque cela n'est pas défini dans l'appel, il fera référence à l'objet global etthis.x
est effectifwindow.x
. Puisque la fonction ne retourne rien,A
aura une valeur deundefined
.Ces différences entre les deux approches se manifestent également si vous sérialisez et dé-sérialisez vos objets Javascript vers / depuis JSON. Les méthodes définies sur le prototype d'un objet ne sont pas sérialisées lorsque vous sérialisez l'objet, ce qui peut être pratique lorsque, par exemple, vous voulez sérialiser uniquement les parties de données d'un objet, mais pas ses méthodes:
Questions connexes :
Sidenote: Il n'y a peut-être pas d'économies de mémoire importantes entre les deux approches, mais l'utilisation du prototype pour partager des méthodes et des propriétés utilisera probablement moins de mémoire que chaque instance ayant sa propre copie.
JavaScript n'est pas un langage de bas niveau. Il peut ne pas être très utile de penser au prototypage ou à d'autres modèles d'héritage comme un moyen de changer explicitement la façon dont la mémoire est allouée.
la source
null
), mais c'est très différent de laprototype
propriété - qui est sur les fonctions et sur laquelle le prototype de toutes les instances est défini lors de leur constructionnew
. Je ne peux pas croire que cela ait vraiment eu 87 votes positifs :-("The language is functional"
êtes-vous sûr que c'est ce que signifie fonctionnel?A
tant que fonction, et l'autre moitié concerne des façons obscures et peu orthodoxes de le faire. quelque chose de simple.Comme d'autres l'ont dit dans la première version, l'utilisation de "this" donne à chaque instance de la classe A sa propre copie indépendante de la méthode de fonction "x". Alors que l'utilisation de "prototype" signifie que chaque instance de classe A utilisera la même copie de la méthode "x".
Voici un code pour montrer cette subtile différence:
Comme d'autres l'ont mentionné, il existe diverses raisons de choisir l'une ou l'autre méthode. Mon échantillon est juste destiné à démontrer clairement la différence.
la source
this
objet, qui est le propriétaire de la méthode. c'est-à-dire que la méthode n'a pas d'objet qui en soit le propriétaire. Dans ce cas, il existe unthis
objet, comme indiqué dans la classe A dans l'exemple.Prenez ces 2 exemples:
contre.
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 taperanyObject.__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'utiliservar 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 versnull
.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: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 placenull
, bonne chance, JavaScript s'en rend compte et vous reviendraundefined
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
: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 aA()
fait ADDITIONNEL en arrière-plan. Lenew
mot-clé a créé un nouvel objet qui fait maintenant référencea1
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'aa1
é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 laprototype
propriété de la fonction . Relisez ça. C'est fondamentalement ceci: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éfinira1
). Donc, maintenant,a1.__proto__
pointe essentiellement vers la même choseA.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: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:Toutes ces choses à l'intérieur
function() { }
vont s'exécuter. Lorsque vous atteignez lathis.hey..
ligne,this
est changé ena1
et vous obtenez ceci: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:a1
.a1 = {}
a1.__proto__
la propriété est assignée pour pointer à la même chose que lesA.prototype
points vers (un autre objet vide {})La fonction
A()
est en cours d'exécution avecthis
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 pourquoithis
changea1
)Maintenant, essayons de créer un autre objet:
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 queA.prototype
vers et surtout, étape 3: la fonctionA()
est à nouveau exécutée, ce qui signifie que la propriété contenant une fonctiona2
sera obtenuehey
.a1
eta2
ont deux propriétés SEPARATE nomméeshey
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 avecnew 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 layoMan
propriété sura1
(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 contientyoMan
, 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: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 ilfunction A()
n'a rien à exécuter. Et si nous le faisons:Il verra qu'il
a1
ne contient pashey
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
eta2
ayant une séparéehey
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__
etFunction.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 laFunctional.prototype
propriété comme deux choses différentes.la source
__proto__
et.prototype
sont des choses totalement différentes.Dans la plupart des cas, ils sont essentiellement les mêmes, mais la deuxième version économise de la mémoire car il n'y a qu'une seule instance de la fonction au lieu d'une fonction distincte pour chaque objet.
Une raison d'utiliser le premier formulaire est d'accéder aux "membres privés". Par exemple:
En raison des règles de portée de javascript, private_var est disponible pour la fonction affectée à this.x, mais pas en dehors de l'objet.
la source
Le premier exemple modifie l'interface pour cet objet uniquement. Le deuxième exemple modifie l'interface pour tous les objets de cette classe.
la source
x
disponible pour tous les objets dont le prototype se voit attribuer une nouvelle instance de A:function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;
Le problème ultime avec l'utilisation
this
au lieu deprototype
est que lors de la substitution d' une méthode, le constructeur de la classe de base fera toujours référence à la méthode substituée. Considère ceci:contre:
Si vous pensez que ce n'est pas un problème, cela dépend si vous pouvez vivre sans variables privées et si vous avez suffisamment d'expérience pour connaître une fuite lorsque vous en voyez une. De plus, avoir à mettre la logique du constructeur après les définitions de méthode n'est pas pratique.
contre:
la source
Chaque objet est lié à un objet prototype. Lorsque vous essayez d'accéder à une propriété qui n'existe pas, JavaScript recherchera dans l'objet prototype de l'objet cette propriété et la renverra si elle existe.
La
prototype
propriété d'un constructeur de fonction fait référence à l'objet prototype de toutes les instances créées avec cette fonction lors de l'utilisationnew
.Dans votre premier exemple, vous ajoutez une propriété
x
à chaque instance créée avec laA
fonction.Dans le deuxième exemple, vous ajoutez une propriété à l'objet prototype vers laquelle toutes les instances créées avec
A
pointent.En conclusion, dans le premier exemple, une copie de la fonction est affectée à chaque instance . Dans le deuxième exemple, une seule copie de la fonction est partagée par toutes les instances .
la source
Quelle est la différence? => Beaucoup.
Je pense que la
this
version est utilisée pour permettre l'encapsulation, c'est-à-dire le masquage des données. Il aide à manipuler des variables privées.Voyons l'exemple suivant:
Maintenant, la
prototype
structure peut être appliquée comme suit:Différents adultes ont des âges différents, mais tous les adultes ont les mêmes droits.
Donc, nous l'ajoutons en utilisant un prototype, plutôt que cela.
Regardons maintenant l'implémentation.
J'espère que cela t'aides.
la source
Le prototype est le modèle de la classe; qui s'applique à toutes les instances futures de celui-ci. Alors que c'est l'instance particulière de l'objet.
la source
Je sais que cela a été répondu à mort, mais je voudrais montrer un exemple réel de différences de vitesse.
Ici, nous créons 2000000 de nouveaux objets avec une
print
méthode dans Chrome. Nous stockons chaque objet dans un tableau. Mettreprint
le prototype prend environ la moitié du temps.la source
Permettez-moi de vous donner une réponse plus complète que j'ai apprise lors d'un cours de formation JavaScript.
La plupart des réponses mentionnaient déjà la différence, c'est-à-dire lorsque le prototypage de la fonction est partagé avec toutes les (futures) instances. Alors que déclarer la fonction dans la classe créera une copie pour chaque instance.
En général, il n'y a pas de bien ou de mal, c'est plus une question de goût ou une décision de conception en fonction de vos besoins. Le prototype est cependant la technique qui est utilisée pour se développer de manière orientée objet, comme j'espère que vous le verrez à la fin de cette réponse.
Vous avez montré deux modèles dans votre question. J'essaierai d'en expliquer deux de plus et d'expliquer les différences, le cas échéant. N'hésitez pas à modifier / étendre. Dans tous les exemples, il s'agit d'un objet de voiture qui a un emplacement et peut se déplacer.
Modèle de décorateur d'objet
Je ne sais pas si ce modèle est toujours pertinent de nos jours, mais il existe. Et c'est bon de le savoir. Vous passez simplement un objet et une propriété à la fonction décorateur. Le décorateur renvoie l'objet avec la propriété et la méthode.
Classes fonctionnelles
Une fonction en JavaScript est un objet spécialisé. En plus d'être invoquée, une fonction peut stocker des propriétés comme tout autre objet.
Dans ce cas, il
Car
s'agit d'une fonction ( pensez également objet ) qui peut être invoquée comme vous en avez l'habitude. Il a une propriétémethods
(qui est un objet avec unemove
fonction). QuandCar
est invoqué, laextend
fonction est appelée, ce qui fait de la magie et étend laCar
fonction (pense objet) avec les méthodes définies dansmethods
.Cet exemple, bien que différent, se rapproche le plus du premier exemple de la question.
Classes prototypiques
Les deux premiers modèles permettent une discussion sur l'utilisation de techniques pour définir des méthodes partagées ou sur l'utilisation de méthodes définies en ligne dans le corps du constructeur. Dans les deux cas, chaque instance a sa propre
move
fonction.Le motif prototypique ne se prête pas bien au même examen, car le partage de fonctions via une délégation de prototype est l'objectif même du motif prototypique. Comme d'autres l'ont souligné, il devrait avoir une meilleure empreinte mémoire.
Cependant, il y a un point intéressant à savoir: chaque
prototype
objet a une propriété de commoditéconstructor
, qui renvoie à la fonction (pensez à l'objet) à laquelle il était attaché.Concernant les trois dernières lignes:
Dans cet exemple, les
Car
liens vers l'prototype
objet, qui relie viaconstructor
àCar
lui-même, c'est- à -direCar.prototype.constructor
estCar
lui - même. Cela vous permet de déterminer quelle fonction constructeur a construit un certain objet.amy.constructor
La recherche échoue et est donc déléguée àCar.prototype
, qui a la propriété constructeur. Et il enamy.constructor
est ainsiCar
.En outre,
amy
est uninstanceof
Car
. L'instanceof
opérateur fonctionne en voyant si l'objet prototype de l'opérande droit (Car
) peut être trouvé n'importe où dans laamy
chaîne prototype ( ) de l'opérande gauche .Certains développeurs peuvent être confus au début. Voir l'exemple ci-dessous:
L'
instanceof
opérateur revientfalse
, carDog
le prototype de ne se trouve nulle part dansfido
la chaîne de prototypes de.fido
est un objet simple qui est créé avec un objet littéral, c'est-à-dire qu'il délègue simplement àObject.prototype
.Modèles pseudoclassiques
C'est vraiment juste une autre forme du modèle prototypique sous forme simplifiée et plus familière pour ceux qui programment en Java par exemple, car il utilise le
new
constructeur.Cela fait la même chose que dans le modèle prototypique, c'est juste du sucre syntaxique au-dessus du modèle prototypique.
Cependant, la principale différence est qu'il existe des optimisations implémentées dans les moteurs JavaScript qui ne s'appliquent que lors de l'utilisation du modèle pseudoclassique. Pensez au modèle pseudoclassique, une version probablement plus rapide du modèle prototypique; les relations d'objet dans les deux exemples sont les mêmes.
Enfin, il ne devrait pas être trop difficile de réaliser comment la programmation orientée objet peut être effectuée. Il y a deux sections.
Une section qui définit les propriétés / méthodes communes dans le prototype (chaîne).
Et une autre section où vous mettez les définitions qui distinguent les objets les uns des autres (
loc
variable dans les exemples).C'est ce qui nous permet d'appliquer des concepts comme la superclasse ou la sous-classe en JavaScript.
N'hésitez pas à ajouter ou modifier. Une fois terminé, je pourrais peut-être en faire un wiki communautaire.
la source
Je pense que @Matthew Crumley a raison. Ils sont fonctionnellement , sinon structurellement, équivalents. Si vous utilisez Firebug pour regarder les objets créés à l'aide
new
, vous pouvez voir qu'ils sont identiques. Cependant, ma préférence serait la suivante. Je suppose que cela ressemble plus à ce à quoi je suis habitué en C # / Java. Autrement dit, définissez la classe, définissez les champs, le constructeur et les méthodes.EDIT ne voulait pas dire que la portée de la variable était privée, j'essayais juste d'illustrer comment je définissais mes classes en javascript. Le nom de la variable a été modifié pour refléter cela.
la source
initialize
andx methods do not refer to the
_instance_var` sur uneA
instance, mais sur une propriété globale. À utiliserthis._instance_var
si vous vouliez utiliser la_instance_var
propriété d'uneA
instance.Comme discuté dans d'autres réponses, c'est vraiment une considération de performance car la fonction dans le prototype est partagée avec toutes les instanciations - plutôt que la fonction créée pour chaque instanciation.
J'ai mis en place un jsperf pour le montrer. Il y a une différence dramatique dans le temps qu'il faut pour instancier la classe, bien qu'elle ne soit vraiment pertinente que si vous faites de nombreuses instances.
http://jsperf.com/functions-in-constructor-vs-prototype
la source
Pensez au langage typé statiquement, les choses sur
prototype
sont statiques et les choses surthis
sont liées à l'instance.la source