Expressions et opérateurs

Dans ce chapitre, nous décrirons les expressions et opérateurs en JavaScript, qui permettent notamment l'affectation, la comparaison, les opérations arithmétiques, binaires, logiques, la manipulation de chaîne de caractères, etc.

Une expression peut être vue comme une unité de code valide qui est résolue en une valeur. Il existe deux types d'expressions, celles qui ont des effets de bord (par exemple l'affectation d'une valeur) et celles qui sont purement évaluées.

L'expression x = 7 fait partie de la première catégorie. Elle utilise l'opérateur = afin d'affecter la valeur sept à la variable x. L'expression elle-même est évaluée avec la valeur 7.

L'expression 3 + 4 fait partie de la deuxième catégorie. Elle utilise l'opérateur + afin d'ajouter 3 et 4 pour produire une valeur : 7. Toutefois, si cette valeur n'est pas utilisée au sein d'une structure plus importante (par exemple avec une déclaration de variable comme const z = 3 + 4), elle sera immédiatement écartée (il s'agit généralement dans ce cas d'une erreur de programmation, car l'évaluation ne produira aucun effet).

Comme les exemples précédents ont permis de montrer, toutes les expressions complexes sont formées avec des opérateurs, tels que = ou +. Dans cette section, nous verrons les opérateurs suivants :

Ces opérateurs assemblent des opérandes formés par des opérateurs de précédence plus haute ou par une des expressions de base. Une liste complète et détaillée des opérateurs et expressions est également disponible dans la référence.

La précédence des opérateurs détermine l'ordre selon lequel ils sont appliqués pour l'évaluation d'une expression. Par exemple :

js
const x = 1 + 2 * 3;
const y = 2 * 3 + 1;

Bien que * et + soit placés dans un ordre différent, ces deux expressions produisent la valeur 7, car * a une précédence qui l'emporte sur celle de +. L'expression formée par les deux opérandes entourant * est donc toujours évaluée en premier ici. Vous pouvez passer outre la précédence des opérateurs en utilisant les parenthèses (ce qui crée une expression groupée, qui est une expression de base). Pour un tableau complet détaillant la précédence des opérateurs et les éventuels pièges, voir la page de référence sur la précédence des opérateurs.

JavaScript possède des opérateurs binaires et unaires, ainsi qu'un opérateur ternaire spécial (l'opérateur conditionnel). Un opérateur binaire a besoin de deux opérandes, un placé avant l'opérateur (à sa gauche) et un placé après l'opérateur (à sa droite) :

opérande1 opérateur opérande2

3 + 4 et x * y sont deux exemples d'expression qui utilisent un opérateur binaire. Sous cette forme, on parle d'opérateur binaire infixe, car l'opérateur est placé entre les deux opérandes. Tous les opérateurs binaires de JavaScript sont infixes.

Un opérateur unaire fonctionne avec un seul opérande, situé avant ou après l'opérateur :

opérateur opérande
opérande opérateur

x++ et ++x sont deux exemples d'expression avec un opérateur unaire. La forme opérateur opérande fonctionne pour les opérateurs unaires postfixes, tandis que la forme opérande opérateur fonctionne pour les opérateurs unaires préfixes. ++ et -- sont les deux seuls opérateurs postfixes de JavaScript. Tous les autres opérateurs unaires, comme ! ou typeof, sont préfixes.

Opérateurs d'affectation

Un opérateur d'affectation affecte une valeur à son opérande gauche selon la valeur de son opérande droit. L'opérateur d'affectation simple est le signe égal (=), qui affecte la valeur de son opérande droit à son opérande gauche. Autrement dit, x = f() est une expression d'affectation qui affecte la valeur de f() à x.

Il existe également des opérateurs d'affectation composites qui sont des raccourcis pour les opérations listées dans le tableau qui suit :

Nom Opérateur Signification
Affectation x = f() x = f()
Affectation après addition x += f() x = x + f()
Affectation après soustraction x -= f() x = x - f()
Affectation après multiplication x *= f() x = x * f()
Affectation après division x /= f() x = x / f()
Affectation du reste x %= f() x = x % f()
Affectation après exponentiation x **= f() x = x ** f()
Affectation après décalage à gauche x <<= f() x = x << f()
Affectation après décalage à droite x >>= f() x = x >> f()
Affectation après décalage à droite non signé x >>>= f() x = x >>> f()
Affectation après ET binaire x &= f() x = x & f()
Affectation après OU exclusif binaire x ^= f() x = x ^ f()
Affectation après OU binaire x |= f() x = x | f()
Affectation après ET logique x &&= f() x && (x = f())
Affectation après OU logique x ||= f() x || (x = f())
Affectation après coalescence des nuls x ??= f() x ?? (x = f())

Affectation aux propriétés

Si une expression est évaluée en un objet, le côté gauche d'une expression d'affectation pourra être utilisé pour des affectations sur les propriétés de cette expression. Par exemple :

js
const obj = {};

obj.x = 3;
console.log(obj.x); // Affiche 3.
console.log(obj); // Affiche { x: 3 }.

const key = "y";
obj[key] = 5;
console.log(obj[key]); // Affiche 5.
console.log(obj); // Affiche { x: 3, y: 5 }.

Pour plus d'informations sur les objets, voir Utiliser les objets.

Si une expression n'est pas évaluée en un objet, les affectations aux propriétés de cette expression ne fonctionneront pas :

js
const val = 0;
val.x = 3;

console.log(val.x); // Affiche undefined.
console.log(val); // Affiche 0.

En mode strict, le fragment de code qui précède déclenchera une exception, car on ne peut pas affecter de propriétés à des valeurs primitives.

C'est une erreur que d'affecter des valeurs à des propriétés non-modifiables ou à des propriétés d'une expression sans propriété (null ou undefined).

Décomposition

Pour les affectations plus complexes, l'affectation par décomposition permet de construire une expression JavaScript pour extraire des données de tableaux ou d'objets avec une syntaxe qui reflète les littéraux utilisés pour la construction de tableaux ou d'objets.

js
const toto = ["un", "deux", "trois"];

// sans décomposition
const un = toto[0];
const deux = toto[1];
const trois = toto[2];

// avec décomposition
const [un, deux, trois] = toto;

Évaluation et imbrication

En général, les affectations sont utilisées pour une déclaration de variable (c'est-à-dire avec les instructions const, let, ou var) ou comme instructions indépendantes.

js
// Déclare une variable x et l'initialise avec le résultat de f().
// Le résultat de l'expression d'affectation "x = f()" est écarté.
let x = f();

x = g(); // Réaffecte le résultat de g() à la variable x.

Toutefois, à l'instar des autres expressions, les expressions d'affectation comme x = f() ont comme résultat une valeur. Bien que cette valeur ne soit généralement pas utilisée, elle peut être utilisée dans une autre expression.

L'enchaînement d'affectations ou l'imbrication d'affectation dans d'autres expressions peut produire des résultats étranges. C'est pour cette raison que certains guides stylistiques JavaScript déconseillent l'enchaînement ou l'imbrication d'affectations). Toutefois, vous pourrez en voir dans certains codes et il est donc important de comprendre comment ils fonctionnent.

En enchaînant ou en imbriquant les expressions d'affectation, le résultat de chaque expression peut être affecté à une autre variable. On peut s'en servir pour journaliser, la placer dans un tableau, un appel de fonction, etc.

js
let x;
const y = (x = f()); // Une notation équivalente serait : const y = x = f();
console.log(y); // Affiche la valeur de retour de l'affectation x = f().

console.log((x = f())); // Affiche la valeur de retour directement.

// Une expression d'affectation peut être imbriquée à n'importe quel
// endroit où les expressions sont autorisées, que ce soit comme
// élément d'un littéral de tableau ou comme argument dans un appel
// de fonction
console.log([0, (x = f()), 0]);
console.log(f(0, (x = f()), 0));

Le résultat de l'évaluation correspond à l'expression à droite du signe = dans la colonne « Signification » du tableau ci-avant. Cela signifie que l'évaluation de x = f() aura comme résultat celui de f(), que l'évaluation de x += f() aura comme résultat la somme x + f(), que celle de x **= f() aura comme résultat x ** f(), etc.

Pour les affectations logiques, x &&= f(), x ||= f(), et x ??= f(), la valeur de retour est celle de l'opération logique sans affectation, soit x && f(), x || f(), et x ?? f(), respectivement.

Lorsqu'on enchaîne ces expressions sans parenthèses ou opérateur de groupement comme les littéraux de tableau, les expressions d'affectation sont groupées de droite à gauche (elles sont associatives à droite), mais sont évaluées de gauche à droite.

On notera que pour les opérateurs d'affectation, à l'exception de =, les valeurs résultantes sont toujours basées sur les valeurs des opérandes avant l'opération.

Par exemple, si on a les fonctions f et g et les variables x et y comme suit :

js
function f() {
  console.log("F!");
  return 2;
}
function g() {
  console.log("G!");
  return 3;
}
let x, y;

Prenons ces trois exemples :

js
y = x = f();
y = [f(), (x = g())];
x[f()] = g();

Exemple d'évaluation n°1

y = x = f() est équivalente à y = (x = f()), car l'opérateur d'affectation est associatif à droite. Toutefois l'évaluation se fait de gauche à droite :

  1. L'évaluation de l'expression d'affectation y = x = f() commence.
    1. L'opérande y à gauche de l'affectation est évalué comme une référence à la variable nommée y.
    2. L'évaluation de l'expression d'affectation x = f() commence.
      1. L'opérande x à gauche de l'affectation est évalué comme une référence à la variable nommée x.
      2. L'appel de fonction f() affiche "F!" dans la console et renvoie la valeur 2.
      3. Ce résultat de f(), 2, est affecté à x.
    3. L'évaluation de l'expression d'affectation x = f() est maintenant terminée. Son résultat est la nouvelle valeur de x, qui est 2.
    4. Ce résultat, 2, est à son tour affecté à y.
  2. L'évaluation de l'expression d'affectation y = x = f() est maintenant terminée. Son résultat est la nouvelle valeur de y, qui est ici 2. La valeur 2 a été affectée à x et y, et la console a affiché "F!".

Exemple d'évaluation n°2

y = [ f(), x = g() ] est également évaluée de gauche à droite :

  1. L'évaluation de l'expression d'affectation y = [ f(), x = g() ] commence.
    1. L'opérande y à gauche de l'affectation est évalué comme une référence à la variable nommée y.
    2. L'évaluation du littéral de tableau [ f(), x = g() ] commence.
      1. L'appel de fonction f() affiche "F!" dans la console et renvoie la valeur 2.
      2. L'évaluation de l'expression d'affectation x = g() commence.
        1. L'opérande x à gauche de l'affectation est évalué comme une référence à la variable nommée x.
        2. L'appel de fonction g() affiche "G!" dans la console et renvoie la valeur 3.
        3. Ce résultat de g(), 3, est affecté à x.
      3. L'évaluation de l'expression d'affectation x = g() est maintenant terminée. Son résultat est la nouvelle valeur de x  3. Ce résultat devient l'élément qui suit dans le littéral de tableau (après le 2 provenant de f()).
    3. L'évaluation du littéral de tableau [ f(), x = g() ] est maintenant terminée. Son résultat est un tableau avec deux valeurs : [ 2, 3 ].
    4. Ce tableau, [ 2, 3 ], est affecté à y.
  2. L'évaluation de l'expression d'affectation y = [ f(), x = g() ] est maintenant terminée. Son résultat est la nouvelle valeur de y, qui est [ 2, 3 ]. 3 est affecté à x, [ 2, 3 ] est affecté à y et la console a affiché "F!" puis "G!".

Exemple d'évaluation n°3

x[f()] = g() est également évaluée de gauche à droite. Dans cet exemple, on part du principe que la valeur de x est déjà un objet (pour plus d'informations sur les objets, voir Utiliser les objets).

  1. L'évaluation de l'expression d'affectation x[f()] = g() commence.
    1. L'évaluation de l'accès à la propriété x[f()] situé à gauche de l'opérateur d'affectation commence.
      1. Le x est évalué comme une référence à la variable nommée x.
      2. Ensuite, l'appel f() affiche "F!" dans la console et renvoie la valeur 2.
    2. L'évaluation de x[f()] est maintenant terminée et son résultat est une variable qui référence la propriété x[2].
    3. Ensuite, l'appel à la fonction g() affiche "G!" dans la console et renvoie la valeur 3.
    4. Ce résultat, 3, est affecté à x[2] (cette étape réussira uniquement si la valeur de x est un objet)?
  2. L'évaluation de l'expression d'affectation x[f()] = g() est maintenant terminée. Son résultat est la nouvelle valeur de x[2], qui est 3. 3 a été affecté à x[2] et la console a affiché "F!" puis "G!".

Éviter les enchaînements d'affectation

L'enchaînement ou l'imbrication d'affectations dans d'autres expressions peut produire d'étranges résultats et c'est pour cette raison que les enchaînements et imbrications d'affectation sont déconseillés).

On évitera en particulier les enchaînements d'affectation dans une instruction const, let, ou var qui ne fonctionnent pas dans la plupart des cas. Seule la variable la plus à gauche sera alors déclarée et les variables intermédiaires dans la chaîne ne seront pas déclarées par l'instruction const/let/var. Ainsi, si on a :

js
const z = y = x = f();

On pourrait penser que cela permet de déclarer les variables x, y, et z. Cependant, seule la variable z est déclarée. y et x sont des références invalides à des variables inexistantes (en mode strict) ou, pire, créent des variables globales pour x et y en mode non-strict.

Opérateurs de comparaison

Un opérateur de comparaison compare ses opérandes et renvoie une valeur logique selon que la comparaison est vraie ou non. Les opérandes peuvent être des nombres, des chaînes de caractères, des booléens ou des objets. Les chaînes de caractères sont comparées selon l'ordre lexicographique standard en utilisant les valeurs Unicode.

Dans la plupart des cas, si les deux opérandes ne sont pas du même type, le moteur JavaScript tentera de les convertir dans un type approprié pour la comparaison. Cela entraîne généralement une comparaison numérique des opérandes.

Les opérateurs === et !== sont des exceptions à cette règle de conversion : ils réalisent des comparaisons d'égalité et d'inégalité strictes. Ces opérateurs n'effectuent pas de conversion des opérandes avant la vérification de l'égalité. Le tableau qui suit décrit les opérateurs de comparaison en utilisant ce fragment de code comme exemple :

js
const var1 = 3;
const var2 = 4;
Opérateurs de comparaison
Opérateur Description Exemples qui renvoient true
Égalité (==) Renvoie true si les opérandes sont égaux (après conversion implicite). 3 == var1
"3" == var1
3 == '3'
Inégalité (!=) Renvoie true si les opérandes sont différents (après conversion implicite). var1 != 4
var2 != "3"
Égalité stricte (===) Renvoie true si les opérandes sont égaux et du même type. Voir également Object.is() et l'égalité en JavaScript. 3 === var1
Inégalité stricte (!==) Renvoie true si les opérandes sont du même type et différents ou s'ils ne sont pas du même type. var1 !== "3"
3 !== '3'
Supériorité stricte (>) Renvoie true si l'opérande gauche est strictement supérieur à l'opérande droit. var2 > var1
"12" > 2
Supériorité (>=) Renvoie true si l'opérande gauche est supérieur ou égal à l'opérande droit. var2 >= var1
var1 >= 3
Infériorité stricte (<) Renvoie true si l'opérande gauche est strictement inférieur à l'opérande droit. var1 < var2
"2" < 12
Infériorité (<=) Renvoie true si l'opérande gauche est inférieur ou égal à l'opérande droit. var1 <= var2
var2 <= 5

Note : => n'est pas un opérateur de comparaison, il s'agit de la notation pour les fonctions fléchées.

Opérateurs arithmétiques

Un opérateur arithmétique combine des opérandes numériques et renvoie une valeur numérique. Les opérateurs arithmétiques standard sont l'addition (+), la soustraction (-), la multiplication (*), et la division (/). Ces opérateurs fonctionnent comme dans la plupart des langages de programmation qui utilisent les nombres flottants (la division par zéro donne notamment Infinity). On pourra par exemple avoir :

js
1 / 2; // 0.5
1 / 2 === 1.0 / 2.0; // true

En plus des opérations arithmétiques standard (+, -, *, /), JavaScript fournit les opérateurs arithmétiques suivants :

Opérateurs arithmétiques
Opérateur Description Exemple
Reste (%) Un opérateur binaire qui renvoie le reste entier de la division des deux opérandes. `12 % 5` renvoie `2`.
Incrément (++) Un opérateur unaire qui ajoute un à son opérande. S'il est utilisé en opérateur préfixe (++x), il renvoie la valeur de son opérande après y avoir ajouté un. S'il est utilisé en opérateur postfixe (x++), il renvoie la valeur de l'opérande avant l'ajout de un. Si x vaut 3, alors ++x définit x avec 4 et renvoie 4, tandis que x++ renvoie 3 puis, uniquement après, définit x avec 4.
Décrément (--) Un opérateur unaire qui soustrait un à son opérande. La valeur de retour est analogue à celle de l'opérateur d'incrément. Si x vaut 3, alors --x définit x avec 2 et renvoie 2, tandis que x-- renvoie 3 puis, uniquement après, définit x avec 2.
Négation unaire (-) Un opérateur unaire qui renvoie l'opposé de l'opérande. Si x vaut 3, -x renvoie -3.
Plus unaire (+) Un opérateur unaire qui tente la conversion de l'opérande en nombre si ce n'est pas déjà une valeur numérique.

+"3" renvoie 3.

+true renvoie 1.

Opérateur d'exponentiation (**) Élève une base donnée par l'opérande gauche à la puissance donnée par l'opérande droit. 2 ** 3 renvoie 8. 10 ** -1 renvoie 0.1.

Opérateurs binaires

Un opérateur binaire traite les opérandes comme des suites de 32 bits (des zéros ou des uns) plutôt que comme des nombres décimaux, hexadécimaux et octaux. Ainsi, le nombre décimal 9 se représente en binaire comme 1001. Les opérateurs binaires effectuent leur opération sur des représentations binaires et renvoient des valeurs numériques.

Le tableau qui suit détaille les opérateurs binaires JavaScript.

Opérateur Utilisation Description
ET binaire a & b Renvoie un à chaque position pour laquelle les bits des deux opérandes valent un.
OU binaire a | b Renvoie un zéro à chaque position pour laquelle les bits des deux opérandes valent zéro.
OU exclusif binaire a ^ b Renvoie un zéro à chaque position pour laquelle les bits sont les mêmes. [Renvoie un à chaque position où les bits sont différents.]
NON binaire ~ a Inverse les bits de l'opérande.
Décalage à gauche a << b Décale la représentation binaire de a de b bits vers la gauche, en ajoutant des zéros à droite.
Décalage à droite avec propagation du signe a >> b Décale la représentation binaire de a de b bits vers la droite, enlevant les bits en trop.
Décalage à droite avec remplissage à zéro a >>> b Décale la représentation binaire de a de b bits vers la droite, enlevant les bits en trop et en ajoutant des zéros à gauche.

Opérateurs binaires logiques

Les opérateurs logiques binaires fonctionnent de la façon suivante :

  • Les opérandes sont convertis en entiers sur 32 bits. Pour les nombres dont la valeur binaire dépasse 32 bits, les bits les plus hauts sont abandonnés. Ainsi, l'entier suivant sur plus de 32 bits sera converti en entier sur 32 bits :

    Avant : 1110 0110 1111 1010 0000 0000 0000 0110 0000 0000 0001
    Après :                1010 0000 0000 0000 0110 0000 0000 0001
    
  • Chaque bit du premier opérande est associé au bit correspondant du second opérande : le premier bit avec le premier bit, le second avec le second et ainsi de suite.

  • L'opérateur est appliqué sur chaque paire ainsi constituée et le résultat est construit en binaire.

Par exemple, la représentation binaire du nombre décimal 9 est 1001, et celle du nombre décimal 15 est 1111. Aussi, quand on utilise les opérateurs binaires sur ces valeurs, on a les résultats suivants :

Expression Résultat Description binaire
15 & 9 9 1111 & 1001 = 1001
15 | 9 15 1111 | 1001 = 1111
15 ^ 9 6 1111 ^ 1001 = 0110
~15 -16 ~ 0000 0000 … 0000 1111 = 1111 1111 … 1111 0000
~9 -10 ~ 0000 0000 … 0000 1001 = 1111 1111 … 1111 0110

On notera que tous les 32 bits sont inversés lors de l'utilisation de l'opérateur NON binaire et que les valeurs avec le bit le plus à gauche à 1 représentent des valeurs négatives (représentation en complément à deux). Aussi, l'évaluation de ~x fournira la même valeur que -x - 1.

Opérateurs de décalage binaire

Les opérateurs de décalage binaire utilisent deux opérandes : celui de gauche est la quantité sur laquelle effectuer le décalage et celui de droite indique le nombre de bits à décaler. La direction de l'opération de décalage dépend de l'opérateur utilisé.

Les opérateurs de décalage convertissent leurs opérandes en entiers sur 32 bits et renvoient un résultat de type Number ou BigInt selon la règle suivante : si l'opérande gauche est de type BigInt, la valeur de retour sera de type BigInt et sinon, la valeur de retour sera de type Number.

Les opérateurs de décalage sont listés dans le tableau suivant.

Opérateurs de décalage binaire
Opérateur Description Exemple
Décalage à gauche (<<) Cet opérateur décale la valeur du premier opérande du nombre de bits indiqué vers la gauche. Les bits en trop sont abandonnés et des bits à 0 sont ajoutés sur la droite. 9<<2 donne 36, car 1001, décalé de 2 bits à gauche vaut 100100 en binaire, ce qui correspond à 36 en décimal.
Décalage à droite avec propagation du signe (>>) Cet opérateur décale la valeur du premier opérande du nombre de bits indiqué vers la droite. Les bits en trop à droite sont abandonnés. Des copies du bit le plus à gauche sont ajoutés sur la gauche. 9>>2 donne 2, car 1001 décalé de 2 bits à droite vaut 10 en binaire, ce qui correspond à 2 en décimal. De même, -9>>2 donne -3, car le signe est conservé.
Décalage à droite avec remplissage à zéro (>>>) Cet opérateur décale la valeur du premier opérande du nombre de bits indiqué vers la droite. Les bits en trop à droite sont abandonnés. Des bits à zéro sont ajoutés sur la gauche. 19>>>2 donne 4, car 10011 décalé de deux bits à droite devient 100 en binaire, ce qui vaut 4 en décimal. Pour les valeurs positives, le décalage à droite avec remplissage à zéro et le décalage à droite avec propagation du signe fourniront le même résultat.

Opérateurs logiques

Les opérateurs logiques sont généralement utilisés avec des valeurs booléennes. Lorsque c'est le cas, la valeur de retour est également booléenne. Plus généralement, les opérateurs && et || renvoient la valeur d'un des deux opérandes (et peuvent donc renvoyer une valeur qui n'est pas un booléen). Ces opérateurs sont décrits dans le tableau suivant.

Opérateurs logiques
Opérateur Utilisation Description
ET logique (&&) expr1 && expr2 Renvoie expr1 si elle peut être convertie en false et renvoie expr2 sinon. Lorsqu'il est utilisé avec des valeurs booléennes, && renvoie true si les deux opérandes valent true et false sinon.
OU logique (||) expr1 || expr2 Renvoie expr1 si elle peut être convertie en true et renvoie expr2 sinon. Lorsqu'il est utilisé avec des valeurs booléennes, || renvoie true si l'un des deux opérandes vaut true et false si les deux valent false.
NON logique (!) !expr Renvoie false si son unique opérande peut être converti en true, renvoie true sinon.

Les expressions équivalentes à false sont celles qui sont évaluées avec la valeur null, 0, NaN, la chaîne vide (""), ou undefined.

Les exemples de code suivant illustrent l'utilisation de l'opérateur ET logique &&.

js
const a1 = true && true; // t && t renvoie true
const a2 = true && false; // t && f renvoie false
const a3 = false && true; // f && t renvoie false
const a4 = false && 3 === 4; // f && f renvoie false
const a5 = "Chat" && "Chien"; // t && t renvoie Chien
const a6 = false && "Chat"; // f && t renvoie false
const a7 = "Chat" && false; // t && f renvoie false

Les exemples de code suivant illustrent l'utilisation de l'opérateur OU logique ||.

js
const o1 = true || true; // t || t renvoie true
const o2 = false || true; // f || t renvoie true
const o3 = true || false; // t || f renvoie true
const o4 = false || 3 === 4; // f || f renvoie false
const o5 = "Chat" || "Chien"; // t || t renvoie Chat
const o6 = false || "Chat"; // f || t renvoie Chat
const o7 = "Chat" || false; // t || f renvoie Chat

Les exemples de code suivant illustrent l'utilisation de l'opérateur NON logique !.

js
const n1 = !true; // !t renvoie false
const n2 = !false; // !f renvoie true
const n3 = !"Chat"; // !t renvoie false

Évaluation en court-circuit

Les expressions logiques étant évaluées de gauche à droite, le moteur d'exécution utilise les courts-circuits dans les cas suivants :

  • false && nimportequoi sera court-circuitée pour être évaluée en false.
  • true || nimportequoi sera court-circuitée pour être évaluée en true.

Les règles logiques garantissent la véracité de ces évaluations. On notera que l'opérande nimportequoi dans les expressions précédentes n'est pas évalué et que tout effet de bord lié à cette évaluation n'aura donc pas lieu.

Pour le deuxième exemple, on pourra utiliser l'opérateur de coalescence des nuls (??) qui fonctionne comme ||, en renvoyant uniquement la deuxième expression lorsque la première est équivalente à null, c'est-à-dire qu'elle vaut null ou undefined.

L'opérateur de coalescence des nuls est donc une meilleure solution lorsqu'il s'agit de fournir des valeurs par défaut, notamment lorsque '' ou 0 sont des valeurs valides.

Opérateurs pour les grands entiers

La plupart des opérateurs numériques peuvent être utilisés avec des valeurs de type BigInt (qui représentent des grands entiers).

js
// Addition de grands entiers
const a = 1n + 2n; // 3n

// Une division entre grands entiers est arrondie à zéro
const b = 1n / 2n; // 0n

// Il n'y a pas de troncature avec les opérations binaires
const c = 40000000000000000n >> 2n; // 10000000000000000n

L'opérateur de décalage à droite non-signé (>>>) est une exception : il n'est pas défini pour les valeurs, car les grands entiers n'ont pas une largeur binaire fixe et n'ont donc pas de bit le plus haut.

js
const d = 8n >>> 2n; // TypeError: BigInts have no unsigned right shift, use >> instead

Il n'est pas possible d'interchanger des nombres et des grands entiers. On ne peut pas les mélanger dans un même calcul.

js
const a = 1n + 2; // TypeError: Cannot mix BigInt and other types

En effet, BigInt n'est pas un sous-ensemble ou un sur-ensemble du type numérique. Les grands entiers ont une plus grande précision mais ne peuvent pas représenter de nombres décimaux. Toute conversion implicite dans un sens ou l'autre entraînerait une perte de précision. Il faudra utiliser une conversion explicite pour indiquer avec quel type on souhaite effectuer l'opération.

js
const a = Number(1n) + 2; // 3
const b = 1n + BigInt(2); // 3n

Il est aussi possible de comparer des grands entiers avec des nombres.

js
const a = 1n > 2; // false
const b = 3 > 2n; // true

Opérateurs pour les chaînes de caractères

En complément des opérateurs de comparaison qui peuvent être utilisés avec les chaînes de caractères, on peut également utiliser l'opérateur de concaténation (+) afin de concaténer deux chaînes de caractères ensemble et renvoyer le résultat de cette concaténation.

Ainsi :

js
console.log("ma " + "chaîne"); // affichera "ma chaîne" dans la console.

L'opérateur d'affectation raccourci += peut également être utilisé pour concaténer des chaînes.

Par exemple :

js
let maChaine = "alpha";
maChaine += "bet"; // sera évalué en "alphabet" et affectera cette valeur à maChaine.

Opérateur conditionnel (ternaire)

L'opérateur conditionnel est le seul opérateur JavaScript à prendre trois opérandes. Il permet de se résoudre en une valeur ou en une autre selon une condition donnée. Sa syntaxe est la suivante :

js
condition ? val1 : val2;

Si condition est vraie, l'expression sera résolue avec la valeur de val1. Sinon, elle sera résolue avec la valeur de val2. L'opérateur conditionnel peut être utilisé à tout endroit où un opérateur standard peut être utilisé.

Par exemple :

js
const statut = age >= 18 ? "adulte" : "mineur";

Cette instruction affecte la valeur "adulte" à la variable statut si age est supérieur ou égal à 18. Sinon, c'est la valeur "mineur" qui est affectée à statut.

Opérateur virgule

L'opérateur virgule (,) évalue les deux opérandes et renvoie la valeur du dernier opérande. Cet opérateur est généralement utilisé dans les boucles for afin de mettre à jour plusieurs variables à chaque itération de la boucle. L'utiliser ailleurs est considéré comme une mauvaise pratique et on privilégiera plusieurs instructions séparées.

Par exemple, si on a a qui est un tableau en deux dimensions avec 10 éléments pour chaque ligne. Le code qui suit utilise l'opérateur virgule afin de mettre à jour deux variables à la fois. Dans cet exemple, on affiche les valeurs des éléments situés sur la diagonale du tableau :

js
const x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const a = [x, x, x, x, x];

for (let i = 0, j = 9; i <= j; i++, j--) {
  //                              ^
  console.log(`a[${i}][${j}]= ${a[i][j]}`);
}

Opérateurs unaires

Un opérateur unaire fonctionne avec un seul opérande.

delete

L'opérateur delete permet de supprimer une propriété d'un objet. La syntaxe correspondante est :

js
delete objet.propriete;
delete objet[clePropriete];
delete objet[indice];

objet est le nom d'un objet, propriete est une propriété existante, clePropriete est une chaîne de caractères ou un symbole faisant référence à une propriété existante.

Lorsque l'opérateur delete réussit, la propriété est supprimée de l'objet et toute tentative d'accès ultérieur renverra undefined. L'opérateur delete renvoie true si l'opération est possible et false sinon.

js
delete Math.PI; // renvoie false (on ne peut pas supprimer des propriétés non-configurables)

const monObj = { h: 4 };
delete monObj.h; // renvoie true (on peut supprimer des propriétés personnalisées)

Supprimer des éléments de tableau

Comme les tableaux sont également des objets, il est techniquement possible de supprimer des éléments avec delete. Il s'agit toutefois de quelque chose considéré comme une mauvaise pratique. En effet, lorsqu'on supprime une propriété d'un tableau, la longueur n'est pas modifiée et les autres éléments ne sont pas réindexés. Pour un comportement analogue, mieux vaudra réécrire la valeur de l'élément avec undefined. Pour manipuler un tableau, on utilisera plutôt les méthodes associées comme splice().

typeof

L'opérateur typeof s'utilise de la façon suivante :

js
typeof operande;

L'opérateur typeof renvoie une chaîne de caractères qui indique le type de l'opérande non-évalué. operande est une chaîne de caractères, une variable, un mot-clé ou un objet dont on souhaite connaître le type. On peut utiliser des parenthèses autour de l'opérande.

Prenons la définition des variables suivantes :

js
const maFonction = new Function("5 + 2");
const forme = "rond";
const taille = 1;
const toto = ["Pomme", "Mangue", "Orange"];
const jour = new Date();

L'opérateur typeof renvoie les résultats suivants pour ces différentes variables :

js
typeof maFonction; // renvoie "function"
typeof forme; // renvoie "string"
typeof taille; // renvoie "number"
typeof toto; // renvoie "object"
typeof jour; // renvoie "object"
typeof inexistant; // renvoie "undefined"

Pour les mots-clés true et null, l'opérateur typeof renvoie les résultats suivants :

js
typeof true; // renvoie "boolean"
typeof null; // renvoie "object"

Pour un nombre ou une chaîne de caractères, typeof renverra les résultats suivants :

js
typeof 62; // renvoie "number"
typeof "Coucou monde"; // renvoie "string"

Pour les valeurs qui sont des propriétés, typeof renvoie le type de la valeur contenue dans la propriété :

js
typeof document.lastModified; // renvoie "string"
typeof window.length; // renvoie "number"
typeof Math.LN2; // renvoie "number"

Pour les méthodes et fonctions, typeof renverra les résultats suivants :

js
typeof blur; // renvoie "function"
typeof eval; // renvoie "function"
typeof parseInt; // renvoie "function"
typeof shape.split; // renvoie "function"

Pour les objets natifs, typeof renverra les résultats suivants :

js
typeof Date; // renvoie "function"
typeof Function; // renvoie "function"
typeof Math; // renvoie "object"
typeof Option; // renvoie "function"
typeof String; // renvoie "function"

void

L'opérateur void s'utilise d'une des deux façons suivantes :

js
void (expression)
void expression

L'opérateur void indique une expression à évaluer sans renvoyer de valeur. expression est une expression JavaScript à évaluer. Les parenthèses autour de l'expression sont optionnelles, mais c'est une bonne pratique que de les utiliser.

Opérateurs relationnels

Un opérateur relationnel compare ses opérandes et renvoie une valeur booléenne selon le résultat de la comparaison.

in

L'opérateur in renvoie true si la propriété indiquée par l'opérande gauche est présente dans l'objet indiqué par l'opérande droit. La syntaxe est la suivante :

js
nomPropriete in nomObjet;

nomPropriete est une chaîne de caractères, une valeur numérique ou un symbole représentant une propriété, et nomObjet est le nom de l'objet.

Voici quelques exemples utilisant l'opérateur in.

js
// Tableaux
const arbres = ["séquoia", "laurier", "cèdre", "chêne", "érable"];
0 in arbres; // renvoie true
3 in arbres; // renvoie true
6 in arbres; // renvoie false
"séquoia" in arbres; // renvoie false (il faut indiquer l'indice, pas la
// valeur)
"length" in arbres; // renvoie true (length est une propriété native)

// Objets natifs
"PI" in Math; // renvoie true
const maChaine = new String("corail");
"length" in maChaine; // renvoie true

// Objets personnalisés
const maVoiture = { fabricant: "Honda", modele: "Accord", annee: 1998 };
"fabricant" in maVoiture; // renvoie true
"modele" in maVoiture; // renvoie true

instanceof

L'opérateur instanceof renvoie true si l'objet porté par l'opérande gauche est du type indiqué par l'opérande droit. La syntaxe est :

js
nomObjet instanceof typeObjet;

nomObjet est le nom de l'objet dont on souhaite vérifier le type et typeObjet est le type en question, comme Date ou Array.

On utilisera instanceof lorsqu'on souhaite confirmer le type d'un objet à l'exécution. On pourra par exemple gérer différemment une exception selon son type.

Dans l'exemple qui suit, on utilise instanceof pour déterminer si jour est un objet Date. Comme jour est bien un objet de type Date, les instructions conditionnelles sont exécutées.

js
const jour = new Date(1995, 12, 17);
if (jour instanceof Date) {
  // instructions à exécuter
}

Expressions de base

Tous les opérateurs manipulent en fin de compte une ou plusieurs expressions de base. Ces expressions de base utilisent les identifiants et les littéraux, et quelques autres éléments que nous introduisons ci-après. Ils sont décrits plus en détails dans leur page de référence.

this

Le mot-clé this fait référence à l'objet courant. En général, this fait référence à l'objet appelant d'une méthode. this pourra être utilisé avec la notation utilisant le point ou celle utilisant les crochets :

js
this["nomPropriete"];
this.nomPropriete;

Prenons une fonction valider() qui valide la propriété value d'un objet en fonction d'un minimum et d'un maximum :

js
function valider(obj, min, max) {
  if (obj.value < min || obj.value > max) {
    console.log("Valeur invalide !");
  }
}

On peut appeler valider() pour un élément de formulaire sur le gestionnaire d'évènement onChange en utilisant this afin de passer le contrôle du formulaire :

html
<p>Saisissez un nombre entre 18 et 99 :</p>
<input type="text" name="age" size="3" onChange="valider(this, 18, 99);" />

Opérateur de groupement

L'opérateur de groupement, ( ), contrôle la précédence de l'évaluation dans une expression. On peut ainsi prioriser certaines opérations par rapport à d'autres et passer outre la précédence par défaut. Dans l'exemple qui suit, on utilise les parenthèses pour que l'addition ait lieu avant la multiplication.

js
const a = 1;
const b = 2;
const c = 3;

// Précédence par défaut
a + b * c; // 7
// Qui est évalue par défaut comme
a + (b * c); // 7

// On passe outre cette précédence pour
// additionner avant de multiplier
(a + b) * c; // 9

// Ce qui est équivalent à
a * c + b * c; // 9

new

L'opérateur new peut être utilisé afin de créer une instance d'un objet (que ce type d'objet soit défini dans le script ou par l'environnement d'exécution). On utilisera new comme suit :

js
const nomObjet = new TypeObjet(param1, param2, /* …, */ paramN);

super

Le mot-clé super est utilisé pour appeler des fonctions sur le parent d'un objet. Il est utile lorsqu'on manipule des classes afin d'appeler le constructeur parent par exemple.

js
super([arguments]); // On appelle le constructeur parent.
super.fonctionSurLeParent([arguments]);