JavaScript : les Objets
Michel Buffa
Avril 2012
buffa@unice.fr
Nous allons voir comment :
>>>> var myarr = ['red', 'blue', 'yellow', 'purple']; >>> myarr; ["red", "blue", "yellow", "purple"] >>> myarr[0] "red" >>> myarr[3] "purple"
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'
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é :
var objetBizarre = { age: 1, 'oui ou non': 'oui', '!@#$%^&*': true };
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!'); } };
Dans certains langages de programmation (Java par ex) on distingue :
JavaScript utilise les tableaux pour le cas 1, et des objets pour le cas 2
var medor = { nom: 'Benji', parle: function(){ alert('Ouah, Ouah!'); } };
Deux syntaxes possibles :
var heros = { race: 'Tortue', metier: 'Ninja' }; >>> heros.race; "Tortue" >>> heros['race']; "Tortue" >>> heros.age; "age is Undefined"
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"
>>> var key = 'firstname'; >>> book.author[key]; "Joseph"
Dans ce cas on est obligé d'utiliser la syntaxe avec [ et ]...
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"
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
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....
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"
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 ?
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.
Il est temps de dire la vérité :
Cas où l'environnement est un navigateur web :
>>> 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"
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
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"
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...
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
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()
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 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 "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.
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]"
>>> alert(o); >>> alert(o.toString()); // équivalent à la ligne précédente >>> "L'objet en String : " + o // toString() implicite ! "L'objet en String : [object Object]"
>>> o.valueOf() === o true
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]
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()
Les tableaux sont en fait des objets mais spéciaux :
>>> var a = [], o = {}; >>> a.length 0 >>> typeof a.length "number" >>> typeof o.length "undefined"
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 !
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]
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"
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"]
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"]
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
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
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...
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"
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...
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] }"