Par commodité, on sépare habituellement les types de données en primitives (ou types de données de base) et objets. Les objets sont des entités qui ont une identité (ils ne sont égaux qu'à eux-mêmes) et qui associent des noms de propriété à des valeurs. Les propriétés sont appelées slots dans le vocabulaire de la programmation orientée prototype.
Le langage Javascript propose divers types d'objets prédéfinis : tableau, booléen, date, fonction, maths, nombre, objet, expression régulière et chaîne de caractères. D'autres objets prédéfinis sont des objets hôte issus de l'environnement d'exécution. Par exemple dans un navigateur les objets hôtes appartiennent au modèle DOM : fenêtre, document, lien hypertexte, etc.
On peut voir les objets les plus simples comme des dictionnaires. En tant que dictionnaires, ils contiennent des couples clé-valeur, où la clé est une chaîne de caractères et la valeur peut être de n'importe quel type. Les objets simples sont souvent implémentés comme des dictionnaires. Cependant, les objets possèdent des fonctionnalités que n'ont pas les dictionnaires, comme une chaîne prototype.
Exemple de déclaration littérale d'un objet contenant des valeurs :
var monObjet = {nom: 'vistemboir', utilite: 'cet objet ne sert à rien', prix: 5000};
Les propriétés des objets peuvent être créées, lues et écrites, soit avec la notation "point" objet.propriété, soit avec la syntaxe utilisée pour les éléments de tableau :
var etiquette = monObjet.nom; // etiquette contient désormais 'vistemboir' var vente = monObjet['prix']; // vente contient désormais 5000
Les objets peuvent être créés à l'aide d'un constructeur ou de façon littérale. Le constructeur peut utiliser la fonction prédéfinie Object ou une fonction sur mesure. Par convention les noms des fonctions constructeurs commencent par une majuscule.
// création à l'aide du constructeur prédéfini: var premierObjet = new Object(); // création littérale: var objetA = {}; var monObjet = {nom: 'vistemboir', utilite: 'cet objet ne sert à rien', prix: 5000};
Les déclarations littérales d'objets et de tableaux permettent de créer facilement des structures de données adaptées aux besoins :
var maStructure = { nom: { prenom: "Sylvain", patronyme: "Etiré" }, age: 33, distractions: [ "oenologie", "beuveries" ] };
C'est la base de JSON, un format d'échanges de données utilisant une syntaxe inspirée des objets Javascript.
Les constructeurs assignent simplement des valeurs aux slots de l'objet nouvellement créé. Les valeurs assignées peuvent être elles-mêmes d'autres fonctions.
Contrairement à ce qui se passe dans d'autres langages à objets, en Javascript les objets n'ont pas de type. Le constructeur qui sert à créer l'objet n'est pas mémorisé. C'est simplement une fonction qui remplit les slots et le prototype du nouvel objet. Donc le constructeur ne correspond pas à la notion de classe qu'on trouve dans d'autres langages.
Les fonctions sont elles-mêmes des objets, ce qui peut servir à obtenir le même fonctionnement que les "static properties" des langages C++ et Java (cf exemple ci-dessous).
Les fonctions ont une propriété spécifique appelée prototype
(cf paragraphe Héritage ci-dessous).
La destruction d'objet est rarement pratiquée car elle est peu nécessaire. En effet le ramasse-miettes détruit automatiquement les objets qui ne sont plus référencés.
Exemple : manipulation d'objet
function MonObjet(attributA, attributB) { this.attributA = attributA; this.attributB = attributB; } MonObjet.statiqueC = " bleu"; // ajoute à la fonction une propriété statique avec une valeur println(MonObjet.statiqueC); // imprime bleu objet = new MonObjet(' rouge', 1000); println(objet.attributA); // imprime rouge println(objet["attributB"]); // imprime 1000 println(objet.statiqueC); // imprime undefined objet.attributC = new Date(); // ajoute à l'objet une nouvelle propriété avec une valeur delete objet.attributB; // enlève une propriété à l'objet println(objet.attributB); // imprime undefined delete objet; // supprime l'objet entier (rarement utilisé) println(objet.attributA); // déclenche une exception
Une méthode est simplement une fonction qui est assignée à la valeur d'un slot d'objet. Contrairement à ce qui se passe dans de nombreux langages à objets, dans Javascript il n'y a pas de distinction entre la définition d'une fonction et la définition d'une méthode. La différence peut apparaître lors de l'appel de la fonction : une fonction peut être appelée comme une méthode.
Dans l'exemple ci-dessous, la fonction Gloup assigne simplement des valeurs aux slots. Certaines de ces valeurs sont des fonctions. De cette façon Gloup peut assigner des fonctions différentes au même slot de différentes instances du même objet. N.B. Il n'y a pas de prototypage dans cet exemple.
function y2() {return this.xxx + "2 ";} function Gloup(xz) { this.xxx = "yyy-"; if (xz > 0) this.xx = function() {return this.xxx +"X ";}; else this.xx = function() {return this.xxx +"Z ";}; this.yy = y2; } var gloup-un = new Gloup(1); var gloup-zero = new Gloup(0); gloup-zero.xxx = "aaa-"; println(gloup-un.xx() + gloup-zero.xx()); // imprime yyy-X aaa-Z gloup-un.y3 = y2; // assigne à y3 la fonction y2 elle-même et non pas son résultat y2() var bloub={"xxx": "zzz-"} // création d'un objet sans utilisation de constructeur bloub.y4 = y2 // assigne à y4 la fonction y2 elle-même et non pas son résultat y2() println(gloup-un.yy() + gloup-un.y3() + bloub.y4()); // imprime yyy-2 yyy-2 zzz-2 gloup-un.y2(); // déclenche une exception car gloup-un.y2 n'existe pas
Javascript supporte les hiérarchies d'héritage par prototypage de la même façon que le langage self. Chaque objet contient un slot implicite appelé prototype.
Dans l'exemple ci-dessous, la classe Bagnole hérite de la classe Vehicule. Quand on crée l'instance b de la classe Bagnole, une référence à l'instance de base de la classe Vehicule est copiée dans b. La classe Bagnole ne contient pas de valeur pour la fonction roule. Quand la fonction roule est appelée cette fonction est recherchée dans l'arborescence d'héritage et elle est trouvée dans la classe Vehicule. Cela se voit clairement dans l'exemple : si on change la valeur de vehicule.roule, on retrouve cette valeur dans b.roule.
Les implémentations de Javascript basées sur Mozilla permettent d'accéder explicitement au prototype d'un objet grâce à un slot appelé _proto_ comme dans l'exemple ci-dessous.
function Vehicule() { this.roues = function() {println("nombre pair de roues");}; this.roule = function() {println("ça roule");}; } function Bagnole() { this.roues = function() {println("quatre roues");}; // redéfinition de la fonction roues pour la classe Bagnole } vehicule = new Vehicule(); Bagnole.prototype = vehicule; // doit être exécuté avant d'instancier Bagnole b = new Bagnole(); // crée b ce qui copie vehicule dans le slot prototype de b vehicule.roule = function() {println("ça roule sur terrain plat")} // redéfinition de la fonction roule pour l'objet vehicule b.roues(); // imprime quatre roues b.roule(); // imprime ça roule sur terrain plat println(b.roule == Bagnole.prototype.roule); // imprime true println(b.__proto__ == vehicule); // imprime true seulement avec Mozilla
L'exemple suivant montre clairement que les références aux prototypes sont copiées lors de la création de l'instance et que les changements appliqués au prototype se répercutent dans toutes les instances qui s'y réfèrent.
function m1() {return "un ";} function m2() {return "deux ";} function m3() {return "trois ";} function Base() {} Base.prototype.y = m2; alpha = new Base(); println(alpha.y()); // imprime deux function Special(){this.y = m3} special = new Special(); beta = new Base(); Base.prototype = special; // n'a pas d'effet sur alpha et beta qui sont déjà créés println(beta.y()); // imprime deux gamma = new Base(); // gamma est créé avec special dans son slot prototype println(gamma.y()); // imprime trois special.y = m1; // impacte gamma et ses éventuelles classes dérivées println(gamma.y()); // imprime un
En pratique de nombreuses variations d'héritage sont utilisées, ce qui peut être très puissant mais aussi prêter à confusion.