1
- Introduction
|
Nous avons, dans les chapitres qui précèdent, à maintes
fois utilisé des chaînes de caractères
dans le sens dont nous en parlions au §II.7,
c'est à dire, en tant qu'éléments de type simple. Nous
allons voir dans ce chapitre que les chaînes ont pourtant l'apparence
d'objets auxquels sont associés ce qui semble être de nombreuses
méthodes de traitement (modification d'apparence, extraction, recherche
de caractère, de motif, etc.). En tout cas, la syntaxe servant à
les invoquer suit exactement celle d'objets bénéficiant de
propriétés et de méthodes... Qu'en est-il exactement
?
Directement liées aux chaînes de caractères, nous présenterons
ensuite le formalisme lié aux expressions régulières,
l'objet utilisant ce formalisme pour établir des modèles ainsi
que les méthodes et propriétés qui sont liées.
|
2
- Finalement, les chaînes de caractères sont-elles des objets
à part entière ?
|
En préambule, et pour se faire rapidement une opinion
sur le sujet, nous allons utiliser l'opérateur typeof sur une chaîne.
Vous aller donner une valeur quelconque dans une fenêtre de dialogue
(qui a la propriété de toujours restituer une chaîne).
Cette valeur sera affectée à une variable, V dont, en retour,
vous obtiendrez le type. Allons-y...
On voit bien que l'on n'obtient pas le type Object,
comme ce serait effectivement le cas si cette chaîne était véritablement
un objet de type composé, mais un type spécifique : string.
Nous allons à présent redéfinir la
variable par V=new String(<la
valeur que vous venez d'entrer>) et voyons ce que donne
typeof V...
En fait, à chaque type primitif, que ce soit pour
les chaînes, mais aussi pour les nombres et les booléens, correspond
une classe d'objets composés celle-ci constituant une enveloppe
du type primitif correspondant. Pour une variable de type primitif, son enveloppe
va contenir, outre la valeur affectée à la variable, toutes
les propriétés et méthodes de la classe correspondante.
Ainsi, si l'on définit une variable de type simple et que l'on essaie
au travers d'elle d'accéder à des propriétés ou
méthodes de la classe correspondant à son type, JavaScript opère
une conversion implicite et l'objet ainsi créé est utilisé
en ses lieu et place. Cet objet ainsi créé est temporaire. dès
qu'il n'est plus utilisé, sa place est récupérée
et il n'existe donc plus.
Cette transformation s'opère aussi en sens inverse : si nous définissons
un objet de type String et que nous désirons le faire intervenir dans
une expression de concaténation de chaînes utilisant l'opérateur
+, l'objet est transformé vers son alias de type primitif afin que
l'expression soit correctement évaluée.
Ce mécanisme de transformation peut sembler très
élégant, mais s'opérant au coup par coup, on peut craindre
une perte d'efficacité. Mais que l'on se rassure (!...), JavaScript
fait cette transformation très rapidement.
On a vu, toujours au §II.7,
la distinction qui se faisait entre objets de type simple et objets de classe
qui, pour les uns, étaient traités par valeur et, pour les autres,
par référence. A présent que l'on sait que les objets
de type simple ont leur alter ego en tant qu'objet de classe, on peut craindre
un comportement différent selon que l'on utilise l'une ou l'autre forme...
Voyons ce qu'il en est au travers d'un exemple.
Voici deux petits programmes totalement comparables hormis
le fait que dans l'un, on utilise des variables de type simple et dans l'autre
des objets. Il met en jeu des chaînes de caractères car c'est
de cela que nous traitons dans ce chapitre, mais vous pourrez vérifier
que le fonctionnement serait identique avec des nombres (classe Number()).
<script language="JavaScript">
function Conc(x,y){
x+=y;
}
var T = "GA";
var S = T;
Conc(S,T);
alert("S="+S+" T="+T);
</script>
|
<script language="JavaScript">
function Conc(x,y){
x+=y;
}
var T = new String("GA");
var S = T;
Conc(S,T);
alert("S="+S+" T="+T);
</script>
|
On constate que les comportements sont totalement identiques
et, en particulier, que les modifications opérés dans la fonction Conc ne se répercutent pas vers
l'extérieur. Il s'agit bien d'un passage par valeur au sens où
on l'entend habituellement. Il est heureux que les comportements soient
identiques car du fait des transformations implicites dont il a été
question plus haut, si tel n'avait pas été le cas, cela aurait
pu rendre les résultats aléatoires.
|
|
Voyons à présent pour un programme équivalent ce qui
ce serait passé si l'on avait eu affaire à un véritable
objet de classe.
<script language="JavaScript">
function MaChaine(val){
this.valeur=val;
}
function ConcObj(x,y){
x.valeur+=y.valeur;
}
var T = new MaChaine("GA");
var S = T;
ConcObj(S,T);
alert("S="+S.valeur+" T="+T.valeur);
</script>
|
On constate que non seulement la valeur de S.valeur
a bien changé, mais de plus, on s'aperçoit que celle de T.valeur
a changé aussi. Est-ce un bug ? Pas du tout ! C'est au contraire tout
a fait normal si l'on se souvient que toute manipulation d'objets de classe
se fait par référence. Dans cet exemple, que s'est-il passé
?
On a tout d'abord créé un objet de la classe
MaChaine dont la propriété valeur
a été affectée à "GA",
puis cet objet ainsi initialisé a été affecté
à la variable T.T
est donc une référence vers l'objet créé.
On a ensuite créé une nouvelle variable S à laquelle
on a affecté la référence T.
Ainsi, dès lors S et T
pointent vers le seul et même objet qui vient d'être créé.
Dans ConcObj, on modifie la propriété
valeur de celui-ci via la référence S.
Au passage on constate bien que cette modification sera pérenne et
ne sera pas détruite au sortir de la fonction. En effet l'affichage
de S.valeur nous le prouve. Lorsque nous allons
lire pour affichage T.valeur, S
et T pointant vers le même objet, il
est donc normal que la valeur retournée soit elle aussi affectée
par la modification intervenue dans ConcObj.
|
3
- Les propriétés et méthodes associées aux chaînes
Nous avons vu, dans le paragraphe précédent, que les chaînes
avaient, selon leur contexte d'utilisation, soit un statut de type simple, soit
celui d'objet de la classe String. A ce propos, signalons au passage que
ce nom désigne, comme nous l'avons vu dans l'un des exemples précédents
le constructeur de la classe. Parmi les méthodes
qui seront présentées, nous distinguerons deux types : les méthodes
d'environnement qui permettent d'obtenir une copie
de la chaîne cible dans un environnement HTML et les méthodes de
traitement proprement dites qui permettent diverses manipulations sur la chaîne
cible elle-même.
|
La propriété
length
Nous avons déjà rencontré ce terme
lorsque nous avons étudié les tableaux. Dans ce contexte, la
propriété length indiquait la taille dynamique du tableau concerné
(le nombre de ses éléments de premier niveau). Nous avons vu
aussi, dans ce même chapitre, qu'il existait une méthode qui,
appliquée à un tableau, donnait pour résultat la chaîne
composée de ses éléments séparés par un
séparateur (par défaut -,- ou précisé). On voit
donc que tableaux et chaînes ont des points de convergence très
forts et on ne s'étonnera donc pas de rencontrer de nombreuses utilisations
de cette ambivalence.
L'utilisation de cette propriété se présente sous la
forme <chaîne>.length.
Ici, nous vous demandons de fournir une chaîne quelconque et en retour,
une fenêtre vous indiquera le nombre de caractère de celle-ci
:
|
|
Les méthodes d'environnement
Ces méthodes permettent de simplifier l'écriture
d'un programme JavaScript devant générer du code HTML. Sans
rentrer dans des détails inutiles qui feraient référence
au langage HTML, on peut se limiter à citer :
- anchor(<nom>)
- big()
- blink()
- bold()
- fixed()
- fontcolor(<couleur>)
- fontsize(<taille>)
- italics()
- link(<reference>)
- small()
- strike()
- sub()
- sup()
|
|
La méthode charAt(<indice>)
Avant toute chose, et comme nous l'avons vu pour les tableaux,
il convient de préciser que le premier caractère d'une chaîne
se situe à l'indice 0. De façon générale, une
chaîne de n caractères (length)
comporte des éléments depuis l'indice 0 jusqu'à l'indice
n-1. La méthode dont il est ici question permet d'obtenir le caractère
de la chaîne cible dont l'indice est fourni en paramètre.
Par exemple, l'exécution de car="JavaScript".charAt(4)
aura pour effet d'affecter à la variable car
le caractère 'S'. Si la valeur entière
fournie en paramètre est en dehors de l'intervalle [0, length-1], la
valeur rendue est une chaîne vide (de longueur 0).
|
|
La méthode charCodeAt(<indice>)
Cette méthode est semblable dans son fonctionnement
à la précédente. Elle aussi à pour rôle
de rechercher dans la chaîne cible le caractère dont l'indice
est fourni en paramètre, mais ici, ce n'est pas le caractère
lui-même qui est rendu par la méthode, mais son
code Unicode, c'est à dire, une valeur entière comprise
entre 0 et 65535 (codage non signé sur 16 bits).
Nous allons montrer ici trois exemples : les deux premiers
montreront des utilisations de cette méthode. Le troisième montrera
l'utilisation d'une méthode statique, propriété
du constructeur String(), lui-même : fromCharCode().
Celle-ci permet de construire une chaîne de caractères
à partir d'une liste de codes Unicode passée en paramètre.
Le premier test délivre bien le code Unicode de 'a',
97 ; le second aboutit à une erreur car l'indice 10 est au delà
des indices de caractères de la chaîne concernée ; quant
au troisième, il reconstruit une chaîne à partir de la
succession des codes donnés en paramètres et on retrouve bien
'a' de code 97 et les trois caractères suivants dans l'ordre alphabétique.
Notez la façon dont
cette méthode est utilisée et en particulier le préfixe
correspondant au nom du constructeur.
|
|
La méthode concat()
Cette méthode, comme son nom l'indique, permet de
concaténer (accoler) à la chaîne
cible référencée la série de valeurs fournies en
paramètre.
Comportement
de concat()...
|
Résultat
|
"Java".concat("Sc",'ri',"pt")
|
|
"Java".concat("Script",1.3) |
|
On constate que la concaténation s'opère bien
avec des chaînes (jusque là, rien de bien étonnant), mais
aussi avec des valeurs numériques. En effet, toute valeur numérique
passée en paramètre est systématiquement transformée
en chaîne de caractères, ce qui permet ainsi la concaténation.
Notons au passage que l'on peut obtenir les mêmes
résultats d'une façon beaucoup plus simple : en employant l'opérateur
de concaténation de chaîne +.
|
|
Les méthodes indexOf()
et lastIndexOf()
A l'opposé de charAt()
qui permettait de déterminer le caractère apparaissant
à une position particulière dans une chaîne, on pourrait
imaginer une méthode permettant de déterminer l'indice de l'occurrence
d'un caractère dans la chaîne référencé.
En fait, cette méthode est beaucoup plus puissante que cela. Elle permet,
en effet, d'indiquer l'indice ou débute une
sous chaîne fournie en paramètre dans la chaîne référencée
(bien sûr, si la sous chaîne est réduite à un seul
caractère, on obtient le résultat recherché précédemment).
Mais la sous-chaîne recherchée peut apparaître plusieurs
fois dans la chaîne référencée. Dans ces conditions,
comment balayer toutes les occurrences sans devoir tronquer, au fur et à
mesure, le début de la chaîne de référence (en
supposant que la recherche s'opère de gauche à droite). La méthode
comporte un second paramètre qui précise à partir de
quel indice la recherche doit être entreprise. Ainsi, sans modifier
la chaîne source, on pourra énumérer toutes les occurrences
d'une sous-chaîne précisée et en indiquer l'indice de
début.
Finalement, la forme générale d'utilisation de cette méthode
est la suivante :
<chaine
source>.indexOf(<sous chaine>[,<debut>])
On constate que le second argument précisant le début
de la recherche est optionnel. En l'absence de celui-ci, la recherche s'opère
à partir de l'indice 0. Si aucune occurrence est trouvée, soit
parce qu'elle n'existe pas, soit parce que la valeur de début est plus
grande que la longueur de la chaîne, cette méthode renvoie la
valeur -1.
ATTENTION
: Dans les versions anciennes de Netscape (< 4), dans le cas
ou le début était hors des limites normales, la méthode retournait
une chaîne vide au lieu de la valeur -1.
Voici un petit texte et le programme qui va nous servir
à compter de nombre de fois où la lettre 'e' apparaît...
Le
texte... |
"Il est certain que JavaScript est un langage simple
à apprendre, mais il n'en est pas pour autant rudimentaire... "
|
Le
programme... |
var Compt = 0;
var Deb = Texte.indexOf('e');
while (Deb != -1) {
Compt++;
Deb = Texte.indexOf('e',++Deb);
}
alert("Nbre d'occurrences de 'e' : " + Compt); |
Le
résultat... |
|
Nous venons de voir la méthode indexOf()
qui opère la recherche de gauche à droite. Il existe une autre
méthode, lastIndexOf() qui elle opère
une recherche de droite à gauche. L'utilisation de celle-ci
a la même forme que la précédente ; comme elle, elle reçoit
en second paramètre optionnel une valeur entière indiquant l'indice
du début de recherche.
|
|
La méthode
slice(<début>[,<fin>])
Cette méthode n'est pas sans rappeler celle, de même
nom, qui a été présentée lors de l'étude
des tableaux. Elle permet, à partir de la chaîne source référencée,
d'extraire la sous chaîne débutant à
l'indice désigné par le premier paramètre et s'achevant
à l'indice désigné par le second paramètre.
Le second paramètre est optionnel. En l'absence de celui-ci, c'est
toute la fin de la chaîne (à partir de l'indice indiquant le
début) qui est rendue.
Par exemple, "JavaScript".slice(4,6)
rend la valeur 'Sc', "JavaScript".slice(4)
rend la valeur 'Script'. Ces deux exemples
montrent qu'alors que le caractère désigné par l'indice
de début fait partie de la sous-chaîne rendue, celui désigné
par l'indice de fin n'en fait pas partie. Les valeurs données en paramètres
peuvent être négatives. Dans ce cas, elles indiquent des indexations
partant de la fin de la chaîne. On doit interpréter cette valeur,x,
négative, par length + x. Autrement dit, -1 indique le dernier caractère
(indice length-1), -2, l'avant dernier (indice length-2), etc.
ATTENTION
: Il est à noter que le fait d'attribuer une valeur négative
au paramètre de début ne fonctionne pas sous Internet Explorer.
Ce dernier ramène cette valeur à 0 et c'est donc tout le début
de la chaîne qui est rendu.
Voyons quelques utilisations directes
de cette méthode :
Les exemples 1, 2 et 4 fonctionnent bien comme annoncé
précédemment. Sous Netscape, les exemples 3 et 6 provoquent
une erreur car l'indice de début est supérieur à celui
de fin ; par contre, sous Internet explorer, 3 retourne une chaîne vide,
tandis que 6 s'évalue en fonction de la remarque faite plus haut, l'indice
de début (-1) étant remplacé par 0. Cette particularité
d'Internet explorer se manifeste de la même façon pour l'exemple
5 qui se comporte donc lui aussi de manière différente selon
le navigateur utilisé.
|
|
Les méthodes substring(<début>[,<fin>])
et substr(<début>[,<longueur>])
Ces deux méthodes permettent, comme slice(),
d'extraire une sous chaîne de la chaîne
source. substring() constitue l'ancêtre
de slice() en ce sens qu'elle apparaissait
déjà dans JavaScript 1.0 alors que slice() a été
introduite dans JavaScript 1.2. Elle fonctionne exactement de la même
manière (le caractère d'indice <début>
fait partie de la sous-chaîne extraite alors que le caractère
d'indice <fin>
en est exclu), mais elle ne supporte pas les paramètres
négatifs comme nous l'avons vu pour slice().
La méthode substr()
qui est apparue dans JavaScript 1.2 accepte en paramètre une, voire
deux valeurs entières, la première indiquant, comme pour les
précédentes, l'indice de début de la sous-chaîne
à extraire, la seconde, facultative, indiquant la longueur
de celle-ci. Toujours comme pour les précédentes, en l'absence
de ce second paramètre, c'est toute la fin de la chaîne qui est
extraite.
|
|
La méthode split(<séparateur>)
Nous avons étudié dans le chapitre précédent
la méthode join() de
la classe Array(). Rappelons que celle-ci permet
de transformer un tableau en une chaîne de caractères dans laquelle
les éléments du tableau source sont séparés, par
défaut, par le caractère ',' ou, si elle est précisée,
par une chaîne quelconque. La méthode split()
est en quelque sorte la "méthode inverse" puisque à
partir de la chaîne source référencée et
en la découpant sur la base d'un séparateur fourni en paramètre,
elle construit un tableau dont les éléments sont les
sous-chaînes ainsi séparées.
Précisons que le séparateur peut être une chaîne
de caractères éventuellement vide. Dans ce cas particulier,
la chaîne est découpée caractère par caractère.
Il peut aussi prendre la forme d'une expression régulière, mais
nous reviendrons là dessus plus tard.
Pour montrer un exemple d'utilisation de cette méthode,
reprenons le texte utilisé plus haut et calculons le nombre de mots.
Le
texte... |
"Il est certain que JavaScript est un langage simple
à apprendre, mais il n'en est pas pour autant rudimentaire..."
|
Le
programme... |
var Compt = Texte.split(' ').length;
alert("Nbre de mots: " + Compt); |
Le
résultat... |
|
Efficace, n'est ce pas ?... Oui, mais approximatif ! En effet, on n'est pas
à l'abri de caractères d'espacement (' ') doublés, ce
qui aurait pour conséquence de "créer" des mots fantômes.
Par ailleurs, les élisions ne sont pas prises en compte ici :"n'en"
compte pour un seul mot ce qui pour les puristes peut paraître une aberration.
Nous verrons plus loin que l'utilisation d'une expression régulière
nous permettra de pallier à ces travers. |
|
Les méthodes toLowerCase()
et toUpperCase()
Enfin, pour en terminer, voici deux méthodes permettant
de "normaliser" des chaînes de caractères, lorsque
cela est nécessaire, en rendant une copie de
la chaîne source dont tous les caractères alphabétiques
sont convertis en minuscules pour l'une, en
majuscules pour l'autre.
A titre d'exemple d'utilisation, on peut imaginer que parmi
les données saisies dans un formulaire, certaines (initiale de prénom,
nom, ville, etc.) puissent être systématiquement converties en
minuscules. On peut aussi reprendre l'exemple précédent dans
lequel on recherchait le nombre d'occurrences d'une certaine lettre par la
méthode indexOf(). Dans cet exemple,
on fournissait en paramètre une lettre minuscule, mais pour prévoir
l'éventualité d'occurrence de cette même lettre sous forme
d'une majuscule, on aurait du opérer cette recherche sur une copie
du texte où toutes les lettres auraient été transformées
en minuscules.
|
Il y a d'autres méthodes de la classe String, mais nous avons choisi
de ne les présenter que dans le prochain chapitre car, mettant en jeu
les expressions régulières, il serait hasardeux d'en parler avant
d'avoir pris connaissance de celui-ci.
|