JavaScript : les Objets

Michel Buffa
Avril 2012

buffa@unice.fr

Basics

JavaScript orienté objet

Nous allons voir comment :

  • Créer et utiliser des objets,
  • Les fonctions de type constructeurs pour construire des objets,
  • Les objets built-in de JavaScript et leur utilité.

Des tableaux aux objets...

>>>> var myarr = ['red', 'blue', 'yellow', 'purple'];
>>> myarr;
       ["red", "blue", "yellow", "purple"]
>>> myarr[0]
       "red"
>>> myarr[3]
       "purple"
    

En JavasScript un objet = un tableau dont on définit les clés !

var heros = {
  race: 'Tortue',
  metier: 'Ninja'
};        
    

Nom de la variable qui contient l'objet = heros,

Au lieu de [ et ] on utilise { et },

Les éléments de l'objet (ses propriétés) sont séparés par une virgule,

Les paires de clés/valeurs sont séparées par : comme dans race:'Tortue'

Syntaxe de déclaration des propriétés

On peut mettre des simple ou double quotes autour du nom de la propriété, ou rien du tout :

var louis = {age: 40};
var louis = {"age": 40};
var louis = {'age': 40};        
    

Dans certains cas on est obligé de mettre des quotes autour du nom de la propriété :

  • Mot réservé de JavaScript,
  • ccontient des espaces ou des caractères spéciaux,
  • commence par un chiffre

Exemple qui utilise les trois possibilités

var objetBizarre = {
  age: 1,
  'oui ou non': 'oui',
  '!@#$%^&*': true
};
    

Eléments, propriétés, méthodes

Pour des tableaux on parle d'éléments,

Pour des objets on parle de propriétés

Une propriété peut aussi valoir une fonction, dans ce cas on parle de méthode.

var medor = {
  nom: 'Benji',
  parle: function(){
    alert('Ouah, Ouah!');
  }
};
    

Tables de hashage et tableaux associatifs

Dans certains langages de programmation (Java par ex) on distingue :

  1. Un tableau normal, donc les indexs sont des valeurs numériques,
  2. Un tableau associatif dont les clés sont des Strings ou des objets (ex: HashMap en Java)

JavaScript utilise les tableaux pour le cas 1, et des objets pour le cas 2

Accès aux propriétés d'un objet

var medor = {
  nom: 'Benji',
  parle: function(){
    alert('Ouah, Ouah!');
  }
};
    

Deux syntaxes possibles :

  1. medor['nom']
  2. medor.nom (à utiliser si possible)

Accès aux propriétés

var heros = {
  race: 'Tortue',
  metier: 'Ninja'
};
>>> heros.race;
   "Tortue"
>>> heros['race'];
   "Tortue"
>>> heros.age;
   "age is Undefined"
    

Un objet peut contenir un autre objet

var book = {
  name: 'Catch-22',
  published: 1961,
  author: {
    firstname: 'Joseph',
    lastname: 'Heller'
  }
};
>>> book.author.firstname    // à préférer
       "Joseph"
>>> book['author']['lastname']
       "Heller"
>>> book.author['lastname']  // on peut mélanger...
       "Heller"
>>> book['author'].lastname
       "Heller"
    

Autre cas classique où le nom d'une propriété est dans une variable

>>> var key = 'firstname';
>>> book.author[key];
    "Joseph"
    

Dans ce cas on est obligé d'utiliser la syntaxe avec [ et ]...

Appel de méthodes

Une méthode étant une propriété on peut utiliser l'opérateur . ou les crochets

var heros = {
  race: 'Tortue',
  metier: 'Ninja',
  parle: function() {
    return 'Je suis un ' + heros.metier;
  }
}
>>> heros.parle();       // recommandée comme syntaxe
       "Je suis un Ninja"
>>>heros['parle']();     // peu utilisé car moins Java-esque
       "Je suis un Ninja"
    

Modification de propriétés

Contrairement à Java, il est possible en JavaScript d'ajouter ou de supprimer des propriétés une fois l'objet créé.

>>> var heros = {};   // Objet sans propriétés
>>> typeof heros.metier
        "Undefined"
>>> heros.race = 'Tortue';
>>> heros.nom = 'Leonardo';
>>> heros.getNom = function() {return heros.nom;};
>>> hero.getNom();
       "Leonardo"
>>> delete heros.nom;
        true
>>> hero.getNom();
         reference to undefined property heros.nom
    

Utilisation de this

Dans l'exemple précédent le code de la méthode getNom() faisait référence au nom de l'objet, il vaut mieux utiliser le ot clé this.

var heros1 = {
  nom: 'Rafaelo',
  getNom: function() {
    return this.nom;
  }
}
>>> heros1.getNom();
       "Rafaelo"     
    

this correspond à "l'objet courant". Le code est plus portable si on affecte cet objet à une autre variable....

Constructeurs en JavaScript

Un exemple suffit à comprendre le principe (remarquez qu'on nomme la fonction comme une classe Java):

function Heros() {
  this.metier = 'Ninja';
}       
    

On peut maintenant utiliser l'opérateur new :

>>> var monHero = new Heros();
>>>typeof monHero
      "object"
>>> monHero.metier;
       "Ninja"
    

Constructeur avec paramètres

function Heros(nom) {
  this.nom = nom;
  this.metier = 'Ninja';
  this.decrisToi = function() {
    return "Je suis " + this.nom + ", je suis un " + this.metier;
  }
}
>>> var h1 = new Heros('Michelangelo');
>>> var h2 = new Heros('Donatello');
>>> h1.decrisToi();
       "Je suis Michelangelo, je suis un Ninja"
>>> h2.decrisToi();
       "Je suis Donatello, je suis un Ninja"
    

Note : cela commence un peu à ressembler à des classes Java...non ?

Remarques sur les constructeurs

Donner un nom de fonction commençant par une majuscule permet de distinguer les constructeurs !

>>> var monHero = new Heros();
>>>typeof monHero
      "object"        
    

Mais :

>>> var monHero = Heros();
>>>typeof monHero
        "undefined"
    

Normal, car dans ce cas monHero vaut la valeur de retour de Heros() qui ne renvoie rien !

Dans ce cas, le this à l'intérieur de la fonctin Heros() se réfère à ce que l'on appelle l'objet global, qui contient tous les autres.

L'objet global

Il est temps de dire la vérité :

  • Le code JavaScript est exécuté par un environnement (la plupart du temps le navigateur Web, ou plus rarement un Serveur),
  • Cet environnement définit un objet global,
  • Les variables globales sont les propriétés de cet environnement, de même que les fonctions prédéfinies.

Cas où l'environnement est un navigateur web :

  • L'objet global s'appelle window

Continuons sur l'objet global...

>>> var a = 1;
>>> a
    1
>>> window.a
    1
>>> window['a']
    1
>>> function Heros(nom) {this.nom = nom;}
// appel sans new, this = objet global
>>> var h = Heros('Leonardo');
>>> typeof h
      "undefined"
>>> typeof h.nom
      h has no properties h has no properties
>>> nom
      "Leonardo"
>>> window.nom
      "Leonardo"
    

Continuons sur l'objet global...

Si on oublie pas le new :

>>> var h2 = new Heros('Michelangelo'); // On a pas oublié le new !
>>> typeof h2
      "object"
>>> h2.nom
      "Michelangelo"
    

Les fonctions prédéfinies sont des méthodes de l'objet global window :

>>> parseInt('101 dalmatiens')
       101
>>> window.parseInt('101 dalmatiens')
       101
    

La propriété constructor

Quand un objet est créé on lui donne en coulisse une propriété nommée "constructor" :

>>> h2.constructor
       Heros(nom)
>>> typeof a.constructor
       "function"
    

Puisque ce constructeur est une fonction, on peut l'appeler :

>>> var h3 = new h2.constructor('Rafaello'); // construit moi comme h2
>>> h3.nom;
       "Rafaello"
    

La propriété constructor, suite...

Dans le cas où on construit un objet sous sa forme littérale :

>>> var o = {};
>>> o.constructor;
       Object()
>>> typeof o.constructor;
       "function"   
    

Object() est le constructeur built-in de JavaScript. Détails viendront plus loin...

L'opérateur instanceof

instanceof permet de tester avec quel constructeur un objet a été créé

>>> function Heros(){}
>>> var h = new Heros();
>>> var o = {};
>>> h instanceof Herso;
       true
>>> h instanceof Object;
       false             // Pas comme en Java !
>>> o instanceof Object;
       true
    

Fonctions qui retournent des objets (factories)

En plus de la création littérale et de la création par new, on peut aussi créer des objets à l'aide de fonctions qui en renvoient :

function factory(nom) {
  return {
    nom: nom
  };
}
>>> var o = factory('Michel');
>>> o.nom
       "Michel"
>>> o.constructor
       Object()
    

Passage d'objets en paramètres, copie d'objets

Par défaut, toute manipulation d'objet se fait par référence :

>>> var original = {valeur: 1};
>>> var copie = original;
>>> copie.valeur
       1
>>> copy.valeur = 100;
       100
>>> original.valeur
       100        
    

Les deux variables copie et original pointent en fait sur le même objets, ce sont deux références au même objet.

Comparer des objets

Comparer deux objets ne donnera true que s'ils ont la même référence.

Deux objets à priori identiques ne seront pas égaux s'ils n'ont pas la même référence

>>> var fido  = {race: 'chien'};
>>> var benji = {race: 'chien'};
>>> benji === fido
   false
>>> benji == fido
   false
>>> var monChien = benji;
>>> mydog === benji
       true
>>> mydog === fido
     false
    

Objets JavaScript built-in

Objets "wrappers" : Object, Array, Function, Boolean, Number, et String, ils correspondent aux types prédéfinis.

Objets utilitaires : Math, Date, RegExp

Objets pour le debug : Error, etc.

Object

C'est le "père" de tous les objets JavaScript que vous créez. Tous vos objets "héritent" de celui-ci, si on peut parler comme ça...

Ces deux lignes sont équivalentes :

>>> var o = {};
>>> var o = new Object();        
    

On bénéficie de méthodes par défauts comme toString() qui convertit l'objet en Strig:

  >>> o.toString()
     "[object Object]"
    

toString() est assez similaire à son usage Java

>>> alert(o);
>>> alert(o.toString()); // équivalent à la ligne précédente
>>> "L'objet en String : " + o // toString() implicite !
       "L'objet en String : [object Object]"
    

valueOf() renvoie la valeur d'un objet

>>> o.valueOf() === o
      true
    

Array

Array() est une fonction built-in que l'on peut utiliser pour construire des tableaux :

>>> var a = new Array(); // équivalent à var a = [];
>>> var b = new Array(1,2,3,'quatre');
>>> b;
       [1, 2, 3, "quatre"]
    

Piège : si un seul paramètre alors c'est la taille du tableau:

>>> var a2 = new Array(5);
>>> a2;
       [undefined, undefined, undefined, undefined, undefined]
    

Array et objets...

Dans les exemples précédents si on créée des tableaux avec Array() ce sont des objets :

>>> typeof a;
       "object"
    

Et on peut bénéficier des méthodes de Object

>>> a.toString();
       "1,2,3,quatre"
>>> a.valueOf()
       [1, 2, 3, "quatre"]
>>> a.constructor
       Array()
    

Compléments sur les tableaux

Les tableaux sont en fait des objets mais spéciaux :

  • Le nom de leurs propriétés sont des valeurs numériques partant de 0,
  • Ils ont une propriété length qui donne leur longueur,
  • Ils possèdent d'autres propriétés built-in en plus de celles héritées de Object
>>> var a = [], o = {};
>>> a.length
       0
>>> typeof a.length
       "number"
>>> typeof o.length
       "undefined"
    

Tableaux = objet ou pas ?

Hmmm... on peut faire de jolies horreurs quand même :

>>> a[0] = 1; o[0] = 1;
>>> a.prop = 2; o.prop = 2;
>>> a.length
      1
    

La propriété length ne "compte" que les propriétés numériques !

length peut être modifée !

Si plus grand que la taille du tableau

>>> a.length = 5 // peut agrandir le tableau
       5
>>> a
       [1, undefined, undefined, undefined, undefined]
    

Si plus petit, réduit le tableau

>>> a.length = 2;
       2
>>> a
       [1, undefined]
    

Méthodes utiles sur les tableaux

Les plus courantes : sort(), join(), slice(), splice(), push() et pop()

>>> var a = [3, 5, 1, 7, 'test'];
>>> a.push('new') // ajout en fin et renvoie length
       6
>>> a
       [3, 5, 1, 7, "test", "new"]
>>> a.pop() // retire le dernier élément et le renvoie
       "new"
>>> a
       [3, 5, 1, 7, "test"]
>>> var b = a.sort();
>>> b
       [1, 3, 5, 7, "test"]
>>> a
       [1, 3, 5, 7, "test"]
>>> a.join(' et ');
    "1 et 3 et 5 et 7 et test"
    

Suite des fonctions utiles sur les tableaux

slice() renvoie un sous-tableau sans modifier l'original.

>>> a
       [1, 3, 5, 7, "test"]
>>> b = a.slice(1, 3); // éléments 1 et 2
    [3, 5]
>>> b = a.slice(0, 1); // élément 0
    [1]
>>> b = a.slice(0, 2); // éléments 0 et 1
    [1, 3]
>>> a
   [1, 3, 5, 7, "test"]
    

Suite des fonctions utiles sur les tableaux

splice() modifie le tableau en enlevant une tranche, éventuellement en ajoutant aussi de nouveaux éléments.

Les deux premiers paramètres sont les indices de début et fin, les autres paramètres sont les éléments à insérer à la place de la tranche enlevée.

>>> a
   [1, 3, 5, 7, "test"]
>>> b = a.splice(1, 2, 100, 101, 102);
   [3, 5]
>>> a
   [1, 100, 101, 102, 7, "test"]
>>> a.splice(1, 3)
       [100, 101, 102]
>>> a
       [1, 7, "test"]
    

Function

Les fonctions sont des objets en JavaScript, elles ont toutes un constructeur intitulé Function()

>>> function myfunc(a){return a;}
>>> myfunc.constructor
    Function()
    

Les fonctions ont aussi une propriété length qui vaut le nombre de paramètres :

>>> function myfunc(a, b, c){return true;}
>>> myfunc.length
       3
    

Function, suite...

Elles possèdent aussi une propriété caller qui renvoie la fonction appelante :

>>> function A(){return A.caller;}
>>> function B(){return A();}
>>> B();
       B()
    

Si A est appelée depuis l'espace global, l'appelant vaut null :

>>> A();
      null
    

Function, propriété "prototype"

prototype est la propriété la plus importante des fonctions JavaScript

Elle contient un objet,

Elle n'est utile que lorsqu'on utilise une fonction comme un constructeur, par ex: var heros = new Heros('Michel');

Tous les objets créés par cette fonction/constructeur ont une référence vers la propriété prototype de leur constructeur, et considèrent les propriétés de cet objet prototype comme les leurs.

Voyons quelques exemples...

La propriété prototype des fonctions

var unObjet = {
  nom: 'Ninja',
  parle: function(){
    return 'Je suis : ' + this.nom;
  }
}
    

Créons une fonction vide, ah, elle a un prototype !

>>> function F(){}
>>> typeof F.prototype
       "object"
    

La propriété prototype des fonctions

Modifions la valeur de cet objet prototype :

>>> F.prototype = unObjet;
    

Et utilisons F comme un constructeur !

>>> var obj = new F();
>>> obj.nom
       "Ninja"
>>> obj.parle()
       "Je suis Ninja"
    

Intéressant non ? Nous y reviendrons...

Méthodes de l'objet Function

toString() : renvoie le code de la fonction sous forme de String

>>> function myfunc(a, b, c) {return a + b + c;}
>>> myfunc.toString()
       "function myfunc(a, b, c) { 
           return a + b + c; 
        }"
    

toString() et fonctions natives :

>>> eval.toString()
    "function eval() {
        [native code]
    }"
    

A continuer...