En JavaScript, nous avons deux façons de créer une "classe" et de lui donner des fonctions publiques.
Méthode 1:
function MyClass() {
var privateInstanceVariable = 'foo';
this.myFunc = function() { alert(privateInstanceVariable ); }
}
Méthode 2:
function MyClass() { }
MyClass.prototype.myFunc = function() {
alert("I can't use private instance variables. :(");
}
J'ai lu à plusieurs reprises que les gens disant que l'utilisation de la méthode 2 est plus efficace car toutes les instances partagent la même copie de la fonction plutôt que chacune en ayant la sienne. La définition de fonctions via le prototype présente cependant un énorme inconvénient - il est impossible d'avoir des variables d'instance privées.
Même si, en théorie, l'utilisation de la méthode 1 donne à chaque instance d'un objet sa propre copie de la fonction (et utilise donc beaucoup plus de mémoire, sans parler du temps requis pour les allocations) - est-ce ce qui se passe réellement dans la pratique? Il semble qu'une optimisation que les navigateurs Web pourraient facilement faire consiste à reconnaître ce modèle extrêmement courant et à avoir en fait toutes les instances de la référence d'objet la même copie de fonctions définis via ces "fonctions constructeurs". Ensuite, il ne peut donner à une instance sa propre copie de la fonction que si elle est explicitement modifiée ultérieurement.
Toute information - ou, mieux encore, expérience du monde réel - sur les différences de performances entre les deux, serait extrêmement utile.
Voir http://jsperf.com/prototype-vs-this
Déclarer vos méthodes via le prototype est plus rapide, mais le fait de savoir si cela est pertinent est discutable.
Si vous avez un goulot d'étranglement dans les performances de votre application, il est peu probable que ce soit le cas, sauf si vous instanciez 10000+ objets à chaque étape d'une animation arbitraire, par exemple.
Si les performances sont une préoccupation sérieuse et que vous souhaitez micro-optimiser, je suggère de déclarer via prototype. Sinon, utilisez simplement le modèle qui vous convient le mieux.
J'ajouterai que, en JavaScript, il existe une convention de préfixation des propriétés qui sont censées être considérées comme privées avec un trait de soulignement (par exemple _process()
). La plupart des développeurs comprendront et éviteront ces propriétés, à moins qu'ils ne soient prêts à renoncer au contrat social, mais dans ce cas, vous pourriez tout aussi bien ne pas y répondre. Ce que je veux dire, c'est que: vous n'avez probablement pas vraiment besoin de vraies variables privées ...
Dans la nouvelle version de Chrome, this.method est environ 20% plus rapide que prototype.method, mais la création d'un nouvel objet est encore plus lente.
Si vous pouvez réutiliser l'objet au lieu d'en créer toujours un nouveau, cela peut être 50% - 90% plus rapide que la création de nouveaux objets. De plus, l'avantage de l'absence de collecte des ordures, ce qui est énorme:
Cela ne fait une différence que lorsque vous créez de nombreuses instances. Sinon, les performances de l'appel de la fonction membre sont exactement les mêmes dans les deux cas.
J'ai créé un cas de test sur jsperf pour le démontrer:
Vous ne l'avez peut-être pas considéré, mais mettre la méthode directement sur l'objet est en fait mieux d'une manière:
Cependant, la différence de vitesse est presque négligeable. En plus de cela, il est préférable de mettre une méthode sur un prototype de deux manières plus percutantes:
Comme James l'a dit, cette différence peut être importante si vous instanciez des milliers d'instances d'une classe.
Cela dit, je peux certainement imaginer un moteur JavaScript qui reconnaît que la fonction que vous attachez à chaque objet ne change pas entre les instances et ne conserve donc qu'une seule copie de la fonction en mémoire, toutes les méthodes d'instance pointant vers la fonction partagée. En fait, il semble que Firefox fasse une optimisation spéciale comme celle-ci, mais Chrome ne l'est pas.
À CÔTÉ:
Vous avez raison, il est impossible d'accéder aux variables d'instance privées à partir des méthodes internes des prototypes. Donc, je suppose que la question que vous devez vous poser est la suivante: appréciez-vous la possibilité de rendre les variables d'instance vraiment privées plutôt que d'utiliser l'héritage et le prototypage? Personnellement, je pense que rendre les variables vraiment privées n'est pas si important et utiliserais simplement le préfixe de soulignement (par exemple, "this._myVar") pour signifier que bien que la variable soit publique, elle devrait être considérée comme privée. Cela dit, dans ES6, il y a apparemment un moyen d'avoir les deux mondes!
Vous pouvez utiliser cette approche et elle vous permettra d'utiliser prototype
et d'accéder aux variables d'instance.
var Person = (function () {
function Person(age, name) {
this.age = age;
this.name = name;
}
Person.prototype.showDetails = function () {
alert('Age: ' + this.age + ' Name: ' + this.name);
};
return Person; // This is not referencing `var Person` but the Person function
}()); // See Note1 below
Note 1:
La parenthèse appellera la fonction (fonction auto-invoquante) et affectera le résultat au var Person
.
tilisation
var p1 = new Person(40, 'George');
var p2 = new Person(55, 'Jerry');
p1.showDetails();
p2.showDetails();
Cette réponse doit être considérée comme une extension du reste des réponses remplissant les points manquants. L'expérience personnelle et les repères sont intégrés.
En ce qui concerne mon expérience, j'utilise des constructeurs pour construire littéralement mes objets religieusement, que les méthodes soient privées ou non. La principale raison étant que lorsque j'ai commencé, c'était l'approche immédiate la plus simple pour moi, donc ce n'est pas une préférence particulière. Cela aurait pu être aussi simple que j'aime l'encapsulation visible et les prototypes sont un peu désincarnés. Mes méthodes privées seront également affectées en tant que variables dans la portée. Bien que ce soit mon habitude et que les choses restent bien autonomes, ce n'est pas toujours la meilleure habitude et je frappe parfois des murs. En dehors des scénarios farfelus avec un auto-assemblage hautement dynamique en fonction des objets de configuration et de la disposition du code, cela a tendance à être l'approche la plus faible à mon avis, en particulier si les performances sont un problème. Savoir que les internes sont privés est utile mais vous pouvez y parvenir par d'autres moyens avec la bonne discipline. À moins que les performances ne soient sérieusement prises en compte, utilisez tout ce qui fonctionne le mieux pour la tâche à accomplir.
prototype.m = f
, this.m = f
et this.m = function...
ce dernier fonctionne nettement mieux que les deux premiers qui fonctionnent de la même manière. Si la recherche de prototype seule était un problème important, les deux dernières fonctions auraient à la place des performances significatives. Au lieu de cela, quelque chose d'autre d'étrange se passe au moins en ce qui concerne les Canaries. Il est possible que les fonctions soient optimisées en fonction de leurs membres. Une multitude de considérations de performances entrent en jeu. Vous avez également des différences pour l'accès aux paramètres et l'accès aux variables.Avertissements:
En bref, utilisez la méthode 2 pour créer des propriétés/méthodes que toutes les instances partageront. Celles-ci seront "globales" et tout changement apporté sera reflété dans toutes les instances. Utilisez la méthode 1 pour créer des propriétés/méthodes spécifiques à l'instance.
J'aimerais avoir une meilleure référence mais pour l'instant jetez un oeil à this . Vous pouvez voir comment j'ai utilisé les deux méthodes dans le même projet à des fins différentes.
J'espère que cela t'aides. :)