64 votes

Définir des méthodes par le biais d'un prototype ou les utiliser dans le constructeur : une différence de performance réelle ?

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 de nombreuses fois que les gens en 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 d'avoir chacune la sienne. La définition de fonctions via le prototype présente toutefois un énorme inconvénient : elle rend impossible la création de 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 nécessaire aux allocations), est-ce bien ce qui se passe en pratique ? Il semble qu'une optimisation que les navigateurs Web pourraient facilement réaliser consisterait à reconnaître ce modèle extrêmement courant et à faire en sorte que toutes les instances de l'objet fassent référence aux fonctions suivantes le même une copie des fonctions définies via ces "fonctions constructives". Il ne pourrait alors donner à une instance sa propre copie de la fonction que si celle-ci est explicitement modifiée par la suite.

N'importe quel point de vue - ou, encore mieux, l'expérience du monde réel - sur les différences de performance entre les deux, serait extrêmement utile.

65voto

James Points 56229

Voir http://jsperf.com/prototype-vs-this

Déclarer vos méthodes via le prototype est plus rapide, mais on peut se demander si cela est pertinent ou non.

Si vous avez un goulot d'étranglement dans votre application, il est peu probable que ce soit cela, à moins que vous n'instanciez plus de 10000 objets à chaque étape d'une animation arbitraire, par exemple.

Si les performances sont une préoccupation majeure, et que vous souhaitez micro-optimiser, je vous suggère de déclarer via un prototype. Sinon, utilisez simplement le modèle qui vous semble le plus logique.

J'ajouterai qu'en JavaScript, il existe une convention qui consiste à faire précéder les propriétés destinées à être considérées comme privées d'un trait de soulignement (ex. _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 les satisfaire. Ce que je veux dire, c'est que vous n'avez probablement pas vraiment besoin de vrai variables privées...

0 votes

Site web utile. La différence de performances semble en fait assez importante lorsque j'ai effectué ce test. Je ne parlerais pas non plus de "micro-optimisation". Je ne pense pas que l'écriture d'un code de production qui s'échelonne aussi mal que l'utilisation de l'option this ne semble jamais être une bonne idée.

0 votes

C'est intéressant. Le système recherche d'abord une propriété dans l'instance. S'il ne la trouve pas, il la cherche dans le prototype de l'instance. Ainsi, l'appel d'une fonction dans le prototype implique deux recherches. On pourrait penser que c'est plus lent. Et pourtant, l'appel de la fonction prototype semble être beaucoup plus rapide. Savez-vous pourquoi ?

4 votes

@RajV, La méthode prototype n'est déclarée qu'une seule fois. La fonction interne (non-prototype) doit être déclarée à chaque instanciation - je pense que c'est ce qui rend cette approche plus lente. L'appel de la méthode pourrait en fait être plus rapide, comme vous l'avez dit.

2voto

Yongtao Wang Points 39

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 toujours 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 de créer de nouveaux objets. Sans compter l'avantage de l'absence de garbage collection, ce qui est énorme :

http://jsperf.com/prototype-vs-this/59

3 votes

On dirait que jsperf.com n'est plus actif. Avez-vous d'autres mesures de perforation ?

0 votes

JsPerf est de nouveau opérationnel. Ce test dans Chrome 55 donne des résultats identiques aux deux, en utilisant this est trois fois plus rapide dans Firefox 50.

0 votes

Ce test est faux. Dans le premier, vous instanciez la classe puis appelez la méthode à chaque itération. Dans le second, vous instanciez la classe une fois et n'appelez la méthode qu'à chaque itération.

1voto

typeracer Points 1

Cela ne fait une différence que lorsque vous créez beaucoup d'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 :

http://jsperf.com/prototype-vs-this/10

1voto

NikoBellic Points 57

Vous n'y avez peut-être pas pensé, mais le fait de placer la méthode directement sur l'objet est en fait meilleur d'une certaine manière :

  1. Les invocations de méthodes sont très légèrement plus rapide ( jsperf ) puisque la chaîne de prototypes ne doit pas être consultée pour résoudre la méthode.

Toutefois, la différence de vitesse est presque négligeable. En outre, l'application d'une méthode sur un prototype est meilleure à deux égards plus importants :

  1. Création plus rapide d'instances ( jsperf )
  2. Utilise moins de mémoire

Comme l'a dit James, 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 d'une instance à l'autre 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 procède à une optimisation spéciale de ce type, mais pas Chrome.


ASIDE :

Vous avez raison de dire qu'il est impossible d'accéder à des variables d'instance privées depuis des méthodes sur des prototypes. Je suppose donc que la question que vous devez vous poser est de savoir si vous préférez la possibilité de rendre les variables d'instance réellement privées à l'utilisation de l'héritage et du prototypage. Je pense personnellement qu'il n'est pas si important de rendre les variables réellement privées et j'utiliserais simplement le préfixe souligné (par exemple, "this._myVar") pour signifier que, bien que la variable soit publique, elle doit être considérée comme privée. Cela dit, dans ES6, il existe apparemment un moyen d'avoir le meilleur des deux mondes !

0 votes

Votre premier cas de test jsperf est défectueux, car vous ne faites qu'appeler cette méthode sur la même instance, encore et encore. En fait, les moteurs (aussi bien FF que Chrome) faire L'optimisation est en effet très poussée (comme vous l'avez imaginé), et l'inlining qui se produit ici rend votre microbenchmark totalement irréaliste.

0 votes

@Bergi JSPerf dit qu'il exécute le code de configuration "avant chaque boucle de test synchronisée, en dehors de la région de code synchronisée". Mon code de configuration crée une nouvelle instance en utilisant new Cela ne signifie-t-il pas que la méthode n'est pas appelée sur le même objet à plusieurs reprises ? Je ne pense pas que JSPerf serait très utile s'il ne "sandboxait" pas chaque boucle de test.

1 votes

Non, c'est une "boucle de test" - votre code est exécuté dans une boucle pour mesurer la vitesse. Ce test est exécuté plusieurs fois pour obtenir des moyennes, et avant chacun de ces tests et leurs boucles respectives, la configuration est exécutée.

0voto

Johnny Points 763

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 toute modification sera répercutée sur toutes les instances. Utilisez la méthode 1 pour créer des propriétés/méthodes spécifiques à une instance.

J'aimerais avoir une meilleure référence mais pour l'instant, jetez un coup d'œil à ce . 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 vous aidera. :)

0 votes

Votre lien n'est plus valide. Pouvez-vous ajouter du code à votre réponse pour illustrer votre propos ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X