Made with CSS ! Valid HTML 4.01!

Expressions et Opérateurs

Ce chapitre à pour but d'introduire les expressions et opérateurs utilisés en JavaScript. Les utilisateurs de Java, C ou C++ ne seront pas surpris par ce qui suit. Pour les autres, il faudra qu'il prennent l'habitude de manipuler des opérateurs donnant à certaines instructions ou expressions des allures cabalistiques, mais ils se feront assez vite à ce formalisme qui ne présente, au bout du compte, aucune difficulté.

1 - Les Expressions

De façon générale, et quel que soit le langage utilisé, une expression est une suite de caractères pouvant être interprétée de façon à lui associer une valeur. JavaScript n'échappe pas à la règle !!!

Les expressions les plus simples sont celles ne faisant intervenir aucun opérateur. Elles ne contiennent donc qu'un littéral.

Exemple :

0.314e+1                        // littéral numérique
"Expression"                    // littéral chaîne
MonIdent                        // Identificateur de variable
false                           // littéral booléen
function(x){return x+1}         // littéral fonction
{Nom:"Terieur",Prenom:"Alex"}   // littéral objet
[3,"OK",true,[null,12],success] // littéral tableau

Ces expressions simples peuvent être combinées entre elles (ou à d'autres), pour construire des expressions plus complexes, à l'aide d'opérateurs. Certains de ces opérateurs sont communs à différents types de littéraux (bien que leur sémantique soit différente), d'autres sont spécifiques. Par exemple, l'opérateur + peut être utilisé pour concaténer (mettre à la suite) deux chaînes de caractères ou pour additionner deux valeurs numériques. L'opérateur == est un opérateur booléen dont les deux opérandes peuvent être soit numériques, soit chaînes, soit booléens. Ils peuvent même être de types différents... On verra que dans le cas d'une comparaison nombre # chaîne, par exemple, JavaScript opère au préalable une conversion.

2 - Les Opérateurs

Ces opérateurs disposent de deux caractéristiques syntaxico-sémantique : la priorité et le sens d'associativité (gauche-droite ou droite-gauche).
En voici la liste :
 Opérateur  Priorité  Sens Types opérandesFonction
.
( )
[ ]
15GD Objets/ Propriétés
Fonctions / Arguments
Tableau / entier
Accès à une propriété
Appel de fonction
Indexage de tableau
++
--
-
~
!
typeof
delete
void
new
14DG
Unaire
Nombre
Nombre
Nombre
Entier
Booléen
Indifférent
Variable
Indifférent
Nom de constructeur
Pré ou post incrémentation
Pré ou post décrémentation
Moins (Inversion)
Complément à 1 binaire
NON logique
Rend le type de la donnée
Rompt l'accès à une propriété
Rend une valeur indéfinie
Crée un nouvel objet
*
/
%
13GDNombres Multiplication
Division
Reste modulo
+
-
12GDNombresAddition
Soustraction
+12GDChaînesConcaténation
<<
>>
>>>
11GDEntiers Décalage des bits à gauche
Décalage à droite (extension signée)
Décalage à droite (extension à zéro)
<
<=
>
>=
10GDNombres ou Chaînes Inférieurà...
Inférieur ou égal à...
Supérieur à...
Supérieur ou égal à...
==
!=
===
!==
9GDIndifférent Test d'égalité
Test d'inégalité
Test d'identité
Test de non identité
&8GDEntiersET binaire
^7GDEntiersOU exclusif binaire
|6GDEntiersOU binaire
&&5GDBooléensET logique
||4GDBooléensOU logique
? :3DGBooléen / Indifférent / Indifférent
(ternaire)
Opérateur conditionnel
=2DGVariable / IndifférentAffectation
*=, /=, %=
+=, -=
+=
<<=, >>=, >>>=
&=, ^=, |=
2DG Variable / Nombre
Variable / Nombre
Variable / Chaîne
Variable / Entier
Variable / Entier
Affectation avec opération
,1GDIndifférentévaluation multiple

Priorité et sens d'évaluation

L'utilisation des opérateurs dans une expression impose, nous venons de le voir, des contraintes plus ou moins lâches sur les opérandes. Par exemple l'expression C1*C2 n'aura de sens que si C1 et C2 sont (ou peuvent se ramener) à des valeurs numériques. Par contre "C1"*"C2" n'aura pas de sens et provoquera donc une erreur, alors que "12"*"3" sera admis tout comme "12"*3 ou 12*"3". Par contre la valeur finale de l'expression, le résultat, sera d'un type bien précis. Dans le cas présent, quelle que soit l'écriture envisagée, la valeur du résultat sera le nombre 36 !!!

Ce qui vient d'être dit est valable, ainsi que nous allons le voir, pour bien d'autre opérateurs. Le seul opérateur jouant des tours inattendus est l'opérateur + dont la signification contient une ambiguïté.

Exemple :

"3" - 2    // soustraction : opérandes ramenés à des nombres
"3" / '2'  // division : opérandes ramenés à des nombres
'3' << '2' // décalage gauche : opérandes ramenés à des entiers
'3' == 3   // test : opérandes ramenés à des nombres
12 >> "2"  // décalage droite : opérandes ramenés à des entiers
3 % '2'    // modulo : opérandes ramenés à des entiers
"3" + 2    // concaténation : opérandes ramenés à des chaînes
3 & '2'    // ET binaire : opérandes ramenés à des entiers
"4" | 3    // OU binaire : opérandes ramenés à des entiers
Les caractéristiques des opérateurs (priorité ou sens d'associativité) ne sont pas une vue de l'esprit ou une convention prédéfinie qui les impose. Cela découle directement de l'analyseur du langage et en particulier, d'une part du niveau d'introduction des opérateurs dans la grammaire et, d'autre part, de la récursivité (gauche ou droite) des règles concernées. C'est dans ce sens que l'on a parlé plus haut de caractéristiques syntaxico-sémantiques. Pour illustrer cela, considérons un exemple simple :
<Exp>::=<Exp> + <Term>
<Exp>::=<Term>
<Term>::=<Term> * <Num>
<Term>::=<Num>
<Num>::=<Num><Digit>
<Num>::=<Digit>
<Digit>::=0|1|2|....|9
Arbre dériv.

Dans l'arbre de dérivation ci-dessus, nous avons négligé la partie purement lexicale qui n'apporte rien à notre propos. Néanmoins, cet arbre montre bien que seul le niveau d'introduction des opérateurs dans les règles syntaxiques définit leur priorité. Plus bas est le niveau d'introduction d'un opérateur, plus il est prioritaire. Si au lieu d'introduire l'opérateur + dans Exp et * dans Term, on avait procédé à l'inverse, + serait devenu prioritaire sur *. Par ailleurs, on peut constater que les règles sont en récursivité gauche : «pour évaluer l'expression, il faut d'abord évaluer l'expression fille qui est à gauche pour laquelle il faut...». D'où un sens d'évaluation de la gauche vers la droite ! Nous avons rajouté en grisé des parenthèses pour bien montrer l'ordre des évaluations. Mais celles-ci sont tout à fait redondantes et la valeur de cette expression est bien 24.

Les opérateurs arithmétiques

Nous ne nous étendrons pas ici sur les opérateurs arithmétiques habituels, + (addition), * (multiplication), / (division), - binaire (soustraction) ou - unaire (inversion). Nous préciserons toutefois que le % (modulo) ne se contente pas d'opérer sur des entiers, mais permet d'obtenir des reste de divisions rationnelles.
Par exemple, 5.6 % 3.21 = 2.3899999999999997.

Nous nous arrêterons, par contre, sur les opérateurs d'incrémentation (+1) ou de décrémentation (-1). Ces opérateurs, selon qu'ils sont placés avant ou après la variable concernée opèrent l'opération soit à priori, soit à posteriori. Par exemple l'instruction j = i++; est équivalente à j = i; i = i+1; Si l'on avait voulu affecter à j la valeur de i+1, il aurait fallu écrire j = ++i; ce qui est équivalent à i = i+1; j = i; Ce que l'on vient de dire s'applique de la même façon à l'opérateur --.

A noter que si l'utilisation de tels opérateurs intervient dans une chaîne de caractère, vous pouvez rencontrer un problème ...
Ce problème est dû à la confusion qui existe hélas, dans JavaScript, entre le + de concaténation et les opérateurs arithmétiques comportant le signe +.

Pour en revenir aux priorités des opérateurs : après avoir exécuté les instructions les instructions i=3; j=7; l'exécution de i=i+++j va affecter à i la valeur 10 tandis que j conserve la valeur 7 . En effet, ++ étant prioritaire sur +, l'évaluation va s'opérer selon i=(i++)+j et non i=i+(++j). Donc i va bien prendre la valeur 10 et la postincrémentation de i est perdue !!!!! Cette opération se réalise en fait selon le mode suivant : R=i; i=i+1; i=R+j R est un registre de travail. Dans le second cas qui aurait pu avantageusement s'écrire i+=++j, les valeurs finales auraient été i : 11 et j : 8.

Les opérateurs d'égalité et d'identité

L'opérateur d'égalité (==) renvoie une valeur booléenne. Le test s'opère différemment selon que les opérandes sont de type primitif ou de type composé. Pour les types primitifs le test s'opère par valeur, c'est à dire que le résultat du test sera true si et seulement si les arguments sont identiques (nombres ou booléens de même valeur, chaînes comportant une suite identique de caractères)ou peuvent l'être par conversion de chaînes ou de booléens vers des nombres. Dans le cas contraire, le résultat vaudra false. Pour les types composés (tableaux, objets, fonctions), le test s'effectue par référence, c'est à dire que le résultat sera true si les deux arguments font référence aux mêmes objets.

Il faut noter en outre les égalités suivantes :

Enfin, dans le cas où les arguments sont de types différents, les règles suivantes doivent être appliquées (reprenant en cela ce que nous avons vu précédemment) :

On a vu précédemment que toute chaîne avait pour valeur logique true. En conséquence, on serait en droit de penser que l'expression booléenne "X"==true a toujours pour valeur true, quelle que soit la chaîne X
Essayons donc d'évaluer "1" == true. Tout paraît normal...
Essayons "0" == true... Tiens ! La supposition précédente s'avère fausse !!! Mais cela est bien cohérent avec les règles énoncées ci-dessus : par la règle (4) true interprété comme la valeur 1 vérifie donc bien l'égalité avec la chaîne "1"convertie elle-même en 1 par la règle (3) (mais pas la chaîne "0") !!
Dans ces conditions, essayons "0" == false... Correct ! false et "0" ramenés par la règle (4) [resp. (3)] à la valeur 0, on obtient bien pour valeur true .

Finalement, pouvez vous répondre à la question du début : quelle est la valeur de l'expression booléenne "X" == true si la chaîne X est quelconque différente de "1" ? Réfléchissez un peu avant de regarder la réponse.

L'opérateur d'identité (===) a un comportement quelque peu différent selon qu'il porte sur des objets de type primitif ou des objets de type composé.
Les objets de type composé : l'opérateur d'identité se comporte exactement comme l'opérateur d'égalité ;
Les objets de type primitif : l'opérateur d'identité se comporte exactement comme l'opérateur d'égalité, mais il n'opère aucune conversion !!!
Ainsi, tous les exemples du paragraphe précédent retourneront false à un test d'identité. De même null ne sera plus assimilé à l'état indéfini.

En définitive, la relation d'identité sera satisfaite par ses opérandes si et seulement si ceux-ci sont de même type et ont même valeur.

A la limite entre les types primitifs et composés, voyons comment se comportent ces opérateurs à l'aide de 3 exemples où seule l'initialisation de deux variables, s1 et s2 diffère de l'un à l'autre. La suite, identique pour les trois, teste l'égalité et l'identité de ces deux variables...

var s1 = "";
var s2 ="Bonjour"; s1 += s2;
Out = 's1 = ' + s1+'\ns2 = ' + s2;
if (s1 == s2) Out += '\nEgalite : OK';
else Out += '\nEgalite : NON OK';
if (s1 === s2) Out += '\nIdentite : OK';
else out += '\nIdentite : NON OK';
Alert(out)
var s1 = new String("Bonjour");
var s2 = "Bonjour";
Alert(out)
var s1 = new String("Bonjour");
var s2 = new String("Bonjour");
Alert(out)

Le premier exemple montre le comportement des opérateurs d'égalité et d'identité sur deux chaînes, le second, sur une chaîne et un objet chaîne, le troisième sur deux objets chaînes (nous reviendrons plus loin sur la distinction chaîne # objet chaîne)

Les opérateurs de comparaison

Nous ne nous étendrons pas sur les opérateurs de comparaison dans le cas où les opérandes sont numériques. Chacun sait qu'ils vont être utilisés dans les expressions booléennes en général (comme les précédents) et en particulier dans les instructions if ou while, for ou do que nous verrons ensuite. Précisons néanmoins qu'ils n'admettent pour opérandes que des nombres ou des chaînes de caractères (et éventuellement, mais l'intérêt est médiocre, des booléens). Dans le cas d'opérandes de type différents, il y a tentative de conversion vers un nombre. Si la conversion est possible et la comparaison par voie de conséquence, le test est évalué, sinon l'évaluation est false quel que soit l'opérateur.

Exemple :

évaluer les expressions... Résultat
true >= "0"
12 < "Infinity"
"13,6" <= 17
Infinity >= "n'importe quoi"
Nous avons précédemment étudié le comportement des opérateurs d'égalité et d'identité lorsque les opérandes sont des chaînes. Les opérateurs de comparaison sont eux aussi utilisés pour les chaînes de caractères. Dans ce cas, la comparaison s'opère suivant l'ordre lexicographique. Par exemple, "Pascal" < "Perl" est évalué à true. Lorsque les deux chaînes comparées ont une partie commune en tête, c'est la longueur des chaînes qui sert pour l'évaluation. Par exemple, "Java" >= "JavaScript" est évalué à false. Un autre opérateur reçoit pour opérandes des chaînes de caractères : +, opérateur de concaténation. Cet opérateur a pour effet de construire une chaîne en réunissant les chaînes opérandes. Par exemple : "JavaScript, "+'c\'est '+"chouette" a pour valeur "JavaScript, c'est chouette".

Il est important de noter qu'en cas d'ambiguïté, priorité est donné aux opérateurs de chaîne par rapport aux opérateurs de nombre. Ainsi, "12"+7 prend pour valeur, non pas 19, mais "127" ! Dans le cas on l'on veut opérer un calcul de ce type, il faudra auparavant forcer la conversion de la chaîne en nombre. Ainsi, si la variable Str contient la chaîne "12", le calcul arithmétique précédent devra s'écrire Str * 1 + 7. La multiplication force l'évaluation à convertir Str en un nombre avant de l'additionner à 7. Le résultat est alors celui attendu : 19.

Inversement, les opérateurs de comparaison privilégie la conversion vers les nombres. Ainsi "12" > 7 sera bien évalué par true, preuve qu'il y a bien eu conversion de "12" vers 12, car la comparaison lexicographique aurait donné false .

Voyons à présent si vous avez bien assimilé ce que l'on a dit depuis le début de ce chapitre... Pouvez-vous donner la valeur de 4+2+"trouille" ?

Bien ! Et maintenant, vous n'aurez donc plus aucune difficulté pour évaluer l'expression "4"+5+2 > 58 ?!...

Les opérateurs logiques

Les opérateurs logiques sont utilisés dans des expressions booléennes complexes mettant en jeu plusieurs variables. Pour ceux qui connaissent Pascal, ce langage comporte les d'opérateurs AND, OR, NOT qui réclament l'évaluation complète de l'expression dont il font partie. Certaines versions (ou options de compilation) autorisent la génération d'un code optimisé qui retourne une évaluation de l'expression dès qu'elle est possible sans que tous les termes ou facteurs de ladite expression aient forcément été évalués. Les opérateurs logiques de JavaScript sont optimisés.

Les opérateurs binaires

Nous avons montré plus haut quelques expressions utilisant des opérateurs binaires. Ils nécessitent des opérandes numériques (ou ayant subi une conversion adéquate) et interviennent directement sur la configuration binaire du codage en complément à 2 sur 32 bits de ceux-ci. Pour ceux qui ne comprennent rien à ce charabia, ce serait trop long et mal venu ici de l'expliquer. Ils pourront si cela les intriguent consulter mon cours à ce sujet.
Ces opérateurs interviennent dans des utilisations de bas niveau sur des représentations binaires. Cela autorise des réalisations spectaculaires d'efficacité comme, par exemple, l'algorithme de Transformée Rapide de Fourrier (butterfly). Mais pour ceux qui ne se sentent pas concernés par ce genre de subtilité, ils peuvent sauter ce paragraphe, il ne leur en sera pas tenu rigueur...

Dans les exercices qui suivent, essayer d'évaluer vous-même les expressions proposées avant de regarder le résultat (n'hésitez pas à vous référer au tableau du début, en particulier pour la priorité des opérateurs).

Exemple :

évaluer les expressions... Résultat
12-4%3!="1"+(0xD^014)
0?3:null?'6':"1"?100:true?x:12
-2+3^6>>1
~-~3&-~4<<2

Les opérateurs d'affectation

On distingue deux types d'opérateurs d'affectation : l'opérateur d'affectation simple et ceux d'affectation avec calcul. Dans les deux cas, le premier opérande doit être une variable.

 Opérateur   Exemple  Signification Résultat
+= (Num)
+= (Chaîne)
-=
*=
/=
%=
&=
| =
^=
<<=
>>=
>>>=
a=12; a+=3
a="12"; a+="3"
a=12; a-=3
a=12; a*=3
a=12; a/=3
a=13; a%=3
a=12; a&=3
a=12; a |=3
a=14; a ^=3
a=12; a<<3
a=-12; a>>=2
a=-12; a>>>=2
a=a+3
a=a+"3"
a=a-3
a=a*3
a=a/3
a=a%3
a=a&3
a=a|3
a=a^3
a=a<<3
a=a>>2
a=a>>>2
a=15
a=123
a=9
a=36
a=4
a=1
a=0
a=15
a=13
a=48
a=-3
a=1073741821

Opérateurs divers

Nous allons voir ici les derniers opérateurs (mis à part ceux dédiés aux tableaux et aux objets) que propose JavaScript.

Ce petit exemple a plusieurs utilités : il illustre ce qui a été dit au chapitre 2 sur les objets et ce qui vient de l'être au sujet de l'opérateur delete. Celui-ci a bien supprimé la propriété naiss de l'objet Indiv, c'est à dire l'accès vers l'objet Indiv.naiss. Mais cet objet n'a lui-même pas été supprimé car un autre accès, copie pointait vers lui. L'accès à l'objet Indiv.naiss n'existant plus, le test utilisant l'opérateur void nous le confirme et c'est bien alert ("indefini") qui est exécuté.

Chapitre suivant
Chapitre IV