Créer un type JavaScript à partir de son nom : Type.createInstance
Je me suis retrouvé tout à l'heure à devoir créer un type JavaScript en ayant seulement son nom et les arguments du constructor. Voici la signature de la fonction que je devais alors coder.
Type.createInstance = function(typeName, args){
/// <summary>
/// Create an objet of the specified type with the specified arguments.
/// </summary>
/// <param name="typeName" type="String">Name of the type to create.</param>
/// <param name="args" type="Array">Arguments of the constructor.</param>
/// <return type="Object">Returns the specified instance of the object.</return>
}
Comment faire ? Réfléchissons à notre besoin : créer une instance d'un objet en ne connaissant que son nom et un tableau de paramètre. J'ai pensé à la méthode eval qui exécute du code sous sa forme littéral, il m'aurait alors suffit de créer un string contenant le code à exécuter. Cette solution me satisfaisait pas parce que les arguments ne sont pas tous de type simple, il m'aurait fallut serialiser les arguments. Et puis je n'aime pas eval qui souvent ralenti le code.
J'ai donc pensé à la méthode apply de l'objet Function, celle-ci permet d'appeler une fonction d'un autre objet avec un tableau de paramètre, j'avais déjà parlé de cette méthode avec sa cousine call ici : JavaScript : un langage incompris - Appel de fonctions. Après quelques essais voici le résultat.
Type.createInstance = function(typeName, args){
/// <summary>
/// Create an objet of the specified type with the specified arguments.
/// </summary>
/// <param name="typeName" type="String">Name of the type to create.</param>
/// <param name="args" type="Array">Arguments of the constructor.</param>
/// <return type="Object">Returns the specified instance of the object.</return>
var propType = Type.parse(typeName);
var newType = function(){};
newType.prototype = propType.prototype;
var obj = new newType();
propType.prototype.constructor.apply(obj, args);
return obj;
}
Tout d'abord je récupère le type de l'objet grâce à la méthode Type.parse du framework client Microsoft Ajax. Ensuite je déclare un nouveau type vide, je définis le prototype de ce type de façon à ce qu'il corresponde au prototype du type à créer. Puis je créer une instance du type vide. Cet instance possède le prototype recherché, techniquement c'est une instance de l'objet typeName mais sans que son constructeur ait été appelé, il me reste alors a appelé le constructor du type recherché sur mon objet. Cela se fait en utilisant la méthode apply sur la fonction constructor du prototype du type recherché.
Après analyse de mon code je me suis posé une question : que se passe t'il en mémoire ? est-ce que le prototype est partagé par les deux types ? ou alors est-il copié ? J'ai effectué quelques tests et le prototype est bien partagé entre mes deux types ! En utilisant cette méthode seul le constructeur type vide est conservé en mémoire c'est à dire une fonction vide donc pas grand chose.
J'avais trouvé une autre solution que je donne à titre informatif. Je ne vous conseil pas de l'utiliser puisqu'il peut y avoir des problèmes de "thread". En effet cette solution modifie le constructeur du type le temps de la création de l'instance de mon objet. Si ailleurs dans le code, un autre "thread" créer une instance de ce type celui ci ne sera pas appelé et cela peut vite causer des problèmes ...
var propType = Type.parse(typeName);
var constructor = propType.prototype.constructor;
propType.prototype.constructor = function(){};
var obj = new propType();
propType.prototype.constructor = constructor;
return obj;
Je ne suis pas sur d'avoir été bien clair sur l'explication du code, si vous voulez en savoir plus, les commentaires sont là pour ça :-)