I. Avant-propos

JsPackage est un framework JavaScript de programmation orientée objet. Il permet la création d'un ensemble d'objets, tels que des classes, des packages, des interfaces ou encore des namespaces, afin de faciliter la conception et la lisibilité d'applications complexes. De plus, la gestion des éléments privés permet de créer efficacement un code solide et sûr.

Le framework n'utilise que des fonctionnalités de JavaScript 1.8.5 (Ecmascript 5), ce qui le rend compatible avec la majorité des plateformes, telles qu'Internet Explorer (9+), Google Chrome, Firefox, mais aussi Node.js.

Pour utiliser JsPackage, importer simplement le fichier JsPackage.jsJsPackage.js de GitHub dans votre fichier HTML ou votre script Node.js. Ce fichier va ajouter dans l'environnement global (window en HTML ou global dans Node.js) les différents objets nécessaires à l'utilisation de JsPackage.

La documentation est accessible en français et en anglais à cette adresse : wiki.jspackage.netwiki.jspackage.net.

II. Présentation

JsPackage permet de créer des classes. Les classes peuvent avoir des attributs, des méthodes et des constantes privés, protégés ou publics. JsPackage supporte aussi l'héritage et, prochainement, les classes abstraites.

III. Créer une classe

La création des classes passe par l'utilisation de la fonction Class. Cette fonction accepte comme paramètre un objet correspondant à la définition de la classe. Chaque clé de l'objet définit une propriété de la classe, tandis que les valeurs seront les définitions de ses propriétés.

 
Sélectionnez
var OneClass = Class({
      attribute : 1
    , method : function() { return this.attribute}
})

L'exemple ci-dessus crée une classe simple, avec un attribut et une méthode.

Les classes peuvent être instanciées comme n'importe quel objet :

 
Sélectionnez
var instance = new OneClass;

La force de JsPackage réside dans sa possibilité de créer des éléments privés. Cela passe par l'utilisation de descripteurs. Il y a plusieurs descripteurs, tels que :

  • Public ;
  • Private ;
  • Method ;
  • Static ;
  • etc.

Comme on le verra par la suite, ces descripteurs sont des objets que l'on peut chaîner.

Exemple de création d'une classe :

 
Sélectionnez
var OneClass = Class({
    privateAttribute : Private(‘Attribute')
  , getPrivateAttribute : Method(function() {
       return this.privateAttribute
    })
  ,
});

On peut définir une fonction d'initialisation qui sera appelée à chaque instanciation de la classe. La fonction reçoit tous les paramètres fournis à l'instanciation.

 
Sélectionnez
var Square = Class({
    $ : { initialization :
           function(height) {
              this.height = height;
           }
        }
  , shapeType : Attribute.Private.Static('Square')
  , getType : Method.Static(
     function() {
        return this.shapeType;
     })
  , height : Private
  , getHeight : function() {
        return this.height;
     }
  }) ;

var instance1 = new Square(3);

console.log(instance1.getHeight()); // 3
console.log(instance1.getType());   // 'Square'
console.log(Square.getType());      // 'Square'

Dans cet exemple, on crée une classe Square comportant un attribut privé (height) et un attribut privé statique (shapeType) : lors de l'instanciation de la classe, la fonction d'initialisation initialise height.

IV. Définition des propriétés

La définition des membres passe par l'utilisation des descripteurs. Ils sont au nombre de huit et peuvent être appelés de n'importe où dans le programme :

  • Public ;
  • Private ;
  • Protected ;
  • Attribute ;
  • Method ;
  • Static ;
  • Constant ;
  • Final.

Les descripteurs sont chaînables. On peut par exemple définir une méthode statique privée de la manière suivante :

 
Sélectionnez
Private.Static.Method

V. Créer un attribut ou une méthode

Pour créer un attribut, trois syntaxes sont possibles :

 
Sélectionnez
var ClassA = Class({
     a : 1
   , b : Attribute
   , c : Attribute(2)
}) ;

Dans cet exemple, les trois propriétés de la classe OneClass sont des attributs :

  • a : est un attribut parce qu'il n'est ni une fonction ni un descripteur. Il est donc par défaut un attribut public, non statique, non constant, initialisé à 1 ;
  • b : est un attribut public, non statique, non constant qui n'a pas été initialisé (b = undefined) ;
  • c : est un attribut public, non statique, non constant, initialisé à 2.

Pour créer une méthode, deux syntaxes sont possibles :

 
Sélectionnez
var ClassA = Class({
     a : 1
   , b : 2
   , getA : function() {
           return this.a ;
     }
   , getB : Method(
       function() {
         return this.b;
       })
}) ;

var instance = new ClassA;
console.log(instance.getA()); // 1
console.log(instance.getB()); // 2

getA utilise la syntaxe la plus simple : une simple fonction. N'importe quel autre type aurait défini getA comme un attribut. getB utilise le descripteur Method pour déclarer une méthode. Ce descripteur n'accepte que les fonctions comme paramètres.

Une fonction déclarée comme un attribut n'aura accès qu'aux propriétés publiques de la classe.

 
Sélectionnez
var ClassA = Class({
     a : 1
   , b : Private(2)
   , getA : Attribute(
      function() {
         this.a ;
     })
   , getB : Attribute(
       function() {
         return this.b;
       })
}) ;

var instance = new ClassA;
console.log(instance.getA()); // 1
console.log(instance.getB()); // undefined

Les méthodes ne peuvent pas être modifiées : sur un objet ou une classe (méthode statique), il n'est pas possible de remplacer la méthode par une autre.

VI. Définir la visibilité

Trois descripteurs définissent la visibilité :

  • Public ;
  • Protected ;
  • Private.
 
Sélectionnez
var ClassA = Class({
     getA : Method(
        function() {
           return this.a ;
        })
   , a : Protected(10)
}) ;

public : un élément public est visible par tout le monde : tout le monde peut accéder à l'attribut/constant ou exécuter la méthode. protected : un élément protégé est un élément visible uniquement par la classe elle-même, ses instances et les classes et instances qui lui sont dérivées. private : un élément privé n'est visible que par la classe elle-même et ses instances.

VII. À propos des descripteurs

Les descripteurs peuvent être chaînés. Il n'y a pas d'ordre particulier à respecter. Par exemple, ces trois écritures sont valables :

  • Private.Constant.Static ;
  • Constant.Private.Static ;
  • Static.Constant.Private.

La chaîne n'a pas forcément à être cohérente : seul le résultat final compte. Par exemple le descripteur suivant est tout à fait valable et désigne un attribut protégé constant et statique :

 
Sélectionnez
private.public.protected.method.attribute.constant.static

VIII. L'héritage

L'héritage se fait via le paramètre de création : extends. Ce paramètre attend une classe (non finale) comme valeur. Un second paramètre final permet d'indiquer que la classe est une classe finale (c'est-à-dire non extensible).

 
Sélectionnez
var Shape = Class({
     $ : {
      initialization :
       function(x, y) {
          this.x = x;
          this.y = y;
       }
    }
   , x    : Private
   , y    : Private
   , getX : function() {
         return this.x ;
     }
   , getY : function() {
         return this.y ;
     }
});

var Circle = Class({
     $ : {
        extends : Shape
      , initialization :
         function(radius, x, y) {
            this.radius = radius;
            this.$.Super.$.initialization(x, y);
         }
     }
   , radius : Private
   , getRadius : function() {
        return this.radius;
     }
});

var circle1 = new Circle(1, 10, 20);

Comme on peut le voir dans l'exemple précédent, il est possible d'accéder aux propriétés de la classe parente via l'objet this.$.Super.

IX. Accéder aux propriétés privées d'autres objets

Les instances d'une même classe peuvent accéder aux propriétés privées et protégées. Pour cela, on utilisera le mot-clé standard this comme une fonction.

 
Sélectionnez
var Circle = Class({
     $ : { initialization : 
            function(radius) {
               this.radius = radius;
            }
         }
   , radius    : Private
   , getRadius : function() {
         return this.radius;
      }
   , getSurface : Static.Method(
      function(circle) {
         if (!this.$.parentOf(circle))
            return false;
         var radius = this(circle).radius;
         
         return Math.pi * radius * radius;
      })
   }) ;
   
var circle_r10 = new Circle(10);

console.log(Circle.getSurface(circle_r10)); // 314.1592...

this peut être utilisé avec n'importe quoi, y compris une autre classe, l'instance d'une autre classe, un objet qui n'est ni une instance ni une classe, un nombre, une chaîne de caractères, etc. this retournera uniquement les propriétés privées si l'instance ou la classe a le droit d'y accéder. Dans tous les autres cas, il retournera le même objet.

 
Sélectionnez
var TestThis = Class({
  returnThis : Static(function(value) {
     return console.log(this(value));
  })
  , privateAtribute : Static(‘static attribute')
});

TestThis.returnThis(12); // 12
TestThis.returnThis({}); // {}
TestThis.returnThis(TestThis); // TestThis

X. Conclusion

Dans cet article, nous avons vu comment créer des classes avec JsPackage, en implémentant la majorité des fonctionnalités que l'on peut trouver dans d'autres langages orientés objet n'utilisant pas les prototypes :

  • les attributs et méthodes statiques ou non ;
  • la visibilité ;
  • l'héritage.

Dans de futurs tutoriels, nous verrons comment il est possible de créer des packages, des interfaces ou encore des espaces de noms avec JsPackage.

XI. Remerciements

Je tiens tout particulièrement à remercier Cyrille Gachot pour sa relecture et ses conseils sur la documentation, Sophia Tewa et zoom61 pour leur relecture orthographique.