Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Actualités

  • Blog de Cyril DURAND, passionné de JavaScript, Ajax, ASP.net et tout ce qui touche au developpement Web Client-Side.

    View Cyril Durand's profile on LinkedIn

    hit counters

Prototype Vs Closure - optimisation d'une classe en JavaScript

En JavaScript, il y a 2 grandes façons de faire une classe : le mode prototype et le mode closure.

Méthode closure :

var Foo = function(){ this.method = function(){ return 'test'; } }

Méthode prototype :

var Foo = function(){ } Foo.prototype.method = function(){ return 'test'; }

Dans les 2 cas, on peut utiliser l'objet de la même façon.

var foo = new Foo(); foo.method();

Pourquoi est-il conseillé d'utiliser la méthode prototype plutôt que la méthode closure ?

C'est principalement une question de performances. Lors de la création d'un objet "closure", le constructeur est parsé/executé, il y a donc création des différentes méthodes qui appartiennent alors à l'instance de l'objet. Avec une classe "prototype" les méthodes sont déjà instanciées lors de la création du type et appartiennent à l'instance du prototype de l'objet, deux instances d'une même classe partagent le même prototype. Cela a deux incidences :

  • La création de l'objet sera plus longue à cause de la création des méthodes membres.
  • L'instance de l'objet prendra plus de mémoire car les méthodes membres ne sont pas partagées entre les objets.

Pour le prouver j'ai créé 100 000 instances de Foo puis regardé la consommation mémoire ainsi que le temps de création.

  prototype closure
IE7 25 332 ko 568 ms 108 900 ko 976 ms
FF2 39 612 ko 953 ms 56 976 ko 1 236 ms
Opera 9.23 25 008 ko 284 ms 45 882 ko 674 ms
Safari 3.0.3 55 820 ko 240 ms 59 124 ko 333 ms

J'ai relancé le navigateur entre chaque test. Voici le code utilisé :

var foos = []; var d = new Date(); for (var i = 0; i < 100000; i++){ foos.push(new Foo()); } alert((new Date()) - d);

La classe utilisé pour les tests est très simple, mais lorsqu'il s'agit d'objet complexe avec des dizaines de méthodes qui contiennent des dizaines de lignes, alors c'est tout ce contenu qui sera dupliqué en mémoire pour chaque instance.

Si vous utilisez des classes JavaScript et que vous utilisez le mode closure, vous savez maintenant comment optimiser votre application JavaScript ...

Dans les version betas de Microsoft Ajax (Atlas), le mode closure était utilisé. Bertrand Leroy a publié sur son blog un guide de migration du mode closure vers le mode prototype (part 1) et (part 2).

Posted: mardi 16 octobre 2007 13:49 par cyril
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

RaptorXP a dit :

Avec le mode prototype, comment avoir des variables et méthodes privées ?

# octobre 16, 2007 14:07

cyril a dit :

Je viens d'éditer mon post, j'avais oublié le lien de l'article de Bertrand Leroy qui explique comment migrer de closure vers prototype.

En JavaScript il n'existe pas vraiment la notion de public / private. Bien sur on peut émuler ce comportement en faisant

var Foo = function(){

  var _private = 'toto';

  this.method = function(){

      return _private;

  }

}

Mais on ne peut pas s'assurer qu'une méthode est bien celle d'origine et qu'il n'y a pas eu de "hook" dessus.

La notion de portée de variable n'a donc aucun sens ...

Par convention dans ASP.net Ajax, un membre privé commence par _ mais c'est juste une convention ca ne garanti en rien quoi que ce soit.

# octobre 16, 2007 14:47

RaptorXP a dit :

C'était un point que je voulais souliger dans mon post (http://blogs.developpeur.org/raptorxp/archive/2007/10/11/pourquoi-ne-faut-il-plus-utiliser-l-h-ritage-de-classe.aspx) mais comme je n'étais pas sur, je ne l'ai pas évoqué.

Si on veut pouvoir utiliser l'héritage, on est obligé d'utiliser les prototypes, et dans ce cas il est impossible de simuler des variables privées. On doit donc choisir entre l'héritage ou l'encapsulation: c'était un de mes arguments lorsque je disais que JS est mal équipé au niveau programmation objet.

# octobre 16, 2007 15:21

cyril a dit :

Raté ;-)

On peut utiliser l'héritage et les variables privés, ce sera juste un héritage par recopie plutot qu'un héritage par prototype.

/* héritage par "recopie" (mal)*/

var Foo = function(){

   var _private = 'private';

   this.method = function(){

       return _private;

   }

}

var Bar = function(){

   Foo.apply(this, []);

   this.method2 = function(){

       return this.method();

   }

}

var b = new Bar();

alert(b.method2());

cela va bien nous renvoyer "private". Là encore on peut utiliser de l'héritage multiple et on peut aussi appeller une méthode de l'objet de base en créant une nouvelle instance (que je met en cache en membre static de la classe parent)

var Toto = function(){

   Bar.apply(this, []);

   this.method2 = function(){

       var tmp = (Bar._instance=Bar._instance||new Bar()).method2.apply(this, []);

       return tmp + ' yes';

   }

}

Mais je ne conseil pas cette solution pour des raisons de perfs ! Vouloir émuler des membres privées n'a aucun sens en JavaScript puisqu'on peut "hooker" tout ce qu'on veut.

# octobre 16, 2007 16:00

FREMYCOMPANY a dit :

En effet, si on a un membre privé, on fait simplement un obj.privateFieldGetter = function() { return this.anyPublicField; } puis un obj.anyPublicField = theNewValue...

# octobre 16, 2007 18:54

RaptorXP a dit :

Voila ! Et à mon avis (ce n'est peut être pas celui de tout le monde), l'encapsulation (qui passe forcément par des variables privées "sures") est un concept essentiel en POO.

# octobre 16, 2007 19:16

cyril a dit :

Euh Fremy ta solution ne permet pas d'accéder à une variable privé. A ma connaissance on ne peut pas le faire puisqu'il faudrait éxécuter une fonction dans le scope du constructeur et je ne vois pas comment executer une fonction dans un scope précis, je doute que ce soit possible.

Je pense que les variables privées sont "sures".

RaptorXP : Tout dépend de la définition de la POO c'est un concept très vaste. Pour moi JavaScript est beaucoup plus souple niveau objet que C#/Java puisqu'on est pas contraint par les enveloppes qui sont les types, c'est une question de point de vue et d'habitude ;-)

# octobre 16, 2007 20:41

FREMYCOMPANY a dit :

Oui, OK, ma méthode ne permet pas de changer la valeur de 'la variable privée'.

Mais on parlait juste avant "d'encapsulation", donc de la création d'un getProperty et d'un setProperty.

Une fois qu'on crée ces deux méthodes, il est possible de 'faire croire' que la propriété a changé rien qu'en modifiant le couple get/set. Même si la vraie valeur privée n'a pas changée...

Ce n'est donc pas la variable privée qui change, mais son exposition au monde extérieur.

______________________________________

Mais de toute façon, une variable privée n'est jamais sûre à 100%, quoiqu'il arrive, vu que de toute façon, on peut faire aussi ceci (pseudo-code) :

for (funcName in obj &amp;&amp; typeof(obj[funcName])=="function") ==&gt; obj[funcName]=eval((""+obj[funcName]).replace(/(\b)thePrivateVarName(\b)/g, "this.theNewVarName")

On sort alors de la closure toutes les fonctions et on rend inefficace la variable privée. Bien sûr c'est lent et couteux, mais c'est toujours possible.

Par contre, impossible de lire la variable privée si elle n'est pas exposée, je suis bien d'accord sur ce point.

# octobre 17, 2007 14:21
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Disparition de variables de session PHP après une redirection ? par MadMatt le il y a 9 heures et 26 minutes

- [MOSS 2007] Publier ses formulaires InfoPath via feature par Adrien Siffermann le il y a 12 heures et 33 minutes

- Imagine Cup 2008 - Paris - Les résultats par TheSaib .NET blog le il y a 13 heures et 55 minutes

- L'Egypte accueille Imagine Cup 2009 par Code is poetry le il y a 14 heures et 7 minutes

- PowerShell : Mise en ligne de fonctions intéressantes pour SharePoint par Blog Technique de Romelard Fabrice le il y a 15 heures et 14 minutes

- Raccourcis clavier et CRM 4 par Clark, C#, MSCRM, SBS le il y a 19 heures et 20 minutes

- [Silverlight] Comment échanger des données entre une application Silverlight et une page ASP.NET via cookies ? par Thomas Lebrun le il y a 19 heures et 56 minutes

- SharePoint 2007 : Trouver les fichiers CheckOut dans une librairie de document par Philippe Sentenac [MVP SharePoint] le il y a 22 heures et 24 minutes

- [Open XML] Travailler avec Open XML : Linq To XML (Partie 2 - Requêtes/XPath) par Julien Chable le 07-08-2008, 02:05

- [Open XML] Travailler avec Open XML : Linq To XML (Partie 1 - Namespace) par Julien Chable le 07-08-2008, 00:44