I. Avant-propos

ClassObject et PackageObject sont des frameworks JavaScript de programmation orientée objet. ClassObject permet de créer des classes, tandis que PackageObject s'occupe de la création des packages.

ClassObject.js est compatible avec tous les navigateurs prenant en charge JavaScript 1.8.5. Il est en version alpha pour le moment. Cependant, l'ensemble des fonctionnalités décrites ici sont opérationnelles.

II. ClassObject

La création des classes passe par l'utilisation de la fonction Class. Cette fonction accepte comme paramètre un dictionnaire correspondant à la définition de la classe. Chaque clé du dictionnaire définit une propriété de la classe, tandis que les valeurs seront les définitions de ces propriétés. Pour créer une propriété, on utilisera des « descripteurs » :

  • public ;
  • private ;
  • method ;
  • static ;
  • ...

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

Exemple de création d'une classe :

 
Sélectionnez
var UneClasse = Class({: private(1) // Attribut privé initialisé à 1
   ,: attribute.protected.static(2) // Attribut statique protégé initialisé à 2
   ,: undefined // Attribut public non-initialisé
   , getA : function() { // Méthode publique
        return this.;
     }
   , getB : method.static( // Méthode publique statique 
        function() {
           return this.;
        }
   , __init__ : function(3) { // Fonction d'initialisation
      this.c = 3 ;
   }
};

var instance1 = new UneClasse(3;

console.log(instance1.a) ;      // undefined
console.log(instance1.getA()) ; // 1

console.log(instance1.b) ;      // undefined
console.log(instance1.getB()) ; // 2
console.log(UneClasse.getB()) ; // 2

console.log(instance1.c) ;      // 1

II.A. Définition des propriétés

La définition des propriétés 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.

L'initialisation des objets se fait en précisant la méthode __init__. Cette méthode, qui doit être définie comme telle (et non comme un attribut), peut accepter n'importe quel paramètre et ne doit pas être définie comme statique.

Au sein d'une méthode, statique ou non, les propriétés sont accessibles via l'objet this, quelle que soit leur visibilité (publique, privée ou protégée).

II.B. Créer un attribut

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

 
Sélectionnez
var UneClass = Class({: 1
   ,: attribute
   ,: attribute(2)
};


Dans cet exemple, les trois propriétés de la classe UneClasse sont des attributs. a est considéré comme un attribut par défaut. Si on avait précisé une fonction, a aurait été considéré comme une méthode.

Sans aucune précision supplémentaire, ces attributs sont tous trois considérés comme publics, non statiques, non constants, non final.

a et c seront initialisés directement avec des valeurs, respectivement 1 et 2, tandis que b n'aura pas de valeur initiale (en réalité, il vaudra undefined).

II.C. Créer une méthode

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

 
Sélectionnez
var UneClass = Class({
     getA : function() {
        return this.a ;
     }
   , getB : method({
       function() {
          return this.b ;
       }
     }}) ;


On utilise ici une syntaxe simplifiée pour définir getA : on ne précise qu'une fonction. Si on avait utilisé une valeur de tout autre type, getA aurait été défini comme un attribut.

getB utilise la fonction method pour créer une méthode. Cette fonction n'accepte qu'une fonction comme paramètre.

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.

II.D. Définir la visibilité

Trois descripteurs définissent la visibilité :

  • public ;
  • protected ;
  • private.

Exemple :

 
Sélectionnez
var UneClass = Class({
     getA : method(
        function() {
           return this.;
        })
   ,: protected(10)
};

II.E. Propriétés statiques

Pour définir une propriété statique, on utilisera le descripteur static.

Un attribut statique est visible à la fois depuis la classe et depuis n'importe quelle instance de la classe.

Exemple :

 
Sélectionnez
var UneClasse = Class({: static(1)
};

var i = new UneClass() ;
console.log(UneClasse.a) ; // 1
console.log(i.a) ;         // 1

II.F. Constantes

Il est possible de définir des constantes de classes via le descripteur constant.

Exemple :

 
Sélectionnez
var UneClasse = Class({: public.static.constant(1;
};

console.log(UneClasse.a) ; // 1

UneClasse.a = 100 ;

console.log(UneClasse.a) ; // 1

II.G. Propriétés final

Les propriétés final sont des propriétés qui ne peuvent être étendues. On utilisera le descripteur final pour les définir.

II.H. À 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 :

 
Sélectionnez
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 private.public.protected.method.attribute.constant.static est tout à fait valable et désigne un attribut protégé constant et statique.

Il en va de même pour l'initialisation. Par exemple : private.constant(1).static(2) désigne un attribut statique, constant, initialisé à 2.

On pourra aussi initialiser les valeurs via la fonction set des descripteurs (utilisables aussi bien pour les attributs que pour les valeurs).

Il n'est pas obligatoire de préciser la valeur pour les attributs (même s'il est plus logique d'en préciser une pour les constantes).

II.I. L'héritage

L'héritage se fait via une propriété particulière : Class. Cette propriété ne sera pas présente dans l'objet final mais fournit des renseignements sur la classe. Cette propriété doit avoir comme valeur un dictionnaire, acceptant pour le moment deux clés :

  • extends : indique que la classe hérite des propriétés d'une classe mère ;
  • final : indique si oui (true) ou non (false) la classe peut-être étendue (par défaut : false).

Exemple :

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

var ClasseFille = Class({: private(4)
   , getA  : function() { 
        return this.a +;
     }



   , __init__() :
      function() {
        this.super.__init__() ;
        this.b = 12 ;
      }

   , Class : {
         extends : UneClasse
       , final : true}
};

var i = new ClasseFille() ;


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

III. PackageObject

La création de package passe par la fonction Package. Elle accepte comme paramètre un dictionnaire contenant la définition du package.

Exemple :

 
Sélectionnez
pkg = Package({: private.attribute(3)
   , getA : public.method(
        function() {
           return this.;
        }
     }
   , UneClasse : public.Class({: 1
       , getB : function() {
            return this.;
         }
       , getA : static.method(
         function() {
            return this.package.;
         }
        
     })
};


On remarquera que les descripteurs sont les mêmes que pour ClassObject. Ceci n'est pas forcément logique du point de vue de la terminologie (notamment pour les fonctions et les variables qui sont appelées ici des méthodes et des classes). Aussi, cela pourrait changer dans le futur.

Dans la version actuelle, PackageObject ne prend pas en charge les packages imbriqués. Mais, ceci est prévu dans une version prochaine.

IV. Téléchargement et documentation

V. Remerciements

Je remercie FirePrawn et zoom61, des relecteurs attentifs et pointilleux.