Chapitre 1 - Javascript, le langage @ref{title} Cours “Web 4” - Licence Info 3ème année Frédéric Dadeau - frederic.dadeau@univ-fcomte.fr
Un peu d'histoire
Plan du chapitre “Scheme dans le navigateur”

Big bang → Apparition de l'homme → Javascript

Voir https://www.youtube.com/watch?v=RO1Wnu-xKoY et toute la série de vidéos associées

  • Large domination de Netscape, à une époque où le web n'est qu'un moyen de diffuser du contenu.
  • Mark Anderssen, responsable de Netscape, souhaite ajouter de l'interactivité côté client, avec un langage utilisable par les designers et les développeurs.
  • Netscape missionne Brendon Eich pour embarquer... Scheme dans le navigateur (cf. cours PFS)
  • Fusion Sun Microsystems (Java) avec Netscape → Java revient en force, mais est jugé trop complexe tel quel.
  • Finalement, Eich s'inspire de Java pour créer l'ancêtre de Javascript, appelé Mocha, en seulement 10 jours.
De Mocha à Javascript

Mocha emprunte la syntaxe de Java et conserve l'aspect fonctionnel de Scheme, et l'orienté objet par prototypes de Smalltalk.

Le nom Mocha évoluera vers LiveScript, puis vers Javascript pour rappeler Java.

On a donc d'un côté Javascript, souple et léger, et Java, plus lourd mais plus riche, utilisé pour développer des composants web.

A l'époque la concurrence était rude entre Netscape (Navigator) et Microsoft (Internet Explorer). Microsoft a donc proposé sa propre implémentation de Javascript qu'il a nommé JScript.

Bien évidemment, Javascript et JScript ne sont pas compatibles, car les fondements du langage n'ont pas été standardisés... Les développeurs devaient donc développer en double leurs sites, ou apposer un des fameux "Best viewed with Netscape" or "Best viewed with Internet Explorer".

Vers un standard : ECMAScript
  • Première tentative auprès du W3C → échec (contentieux avec Netscape)
  • Seconde approche avec ECMA → succès

ECMA European Computer Manufacturer Association, fondée en 1961, se consacre à la standardisation de systèmes d'information et de communication.

En novembre 1996, Netscape soumet Javascript à ECMA pour en faire une spécification standard :

  • tous les acteurs (développeurs de navigateurs, géants du net, etc.) ont leur mot à dire - comité technique TC39
  • toutes les implémentations du langage sont consistantes au standard ECMA-262 qui définit ECMAScript (le nom Javascript étant propriété d'Oracle qui a racheté Sun Microsystems)

De nos jours, ECMAScript désigne le standard ECMA-262 et Javascript désigne le langage en pratique (son implémentation).

ECMAScript - évolutions
VersionDateDescription
106/1997Première édition
206/1998Modifications cosmétiques
312/1999Ajout des expressions régulières, try/catch, meilleure gestion des chaînes, etc.
4abandonnée
512/2009Mode strict, clarification d'ambiguités dans ES3, getters/setters, support JSON, réflexion sur les objets
6 / ES201506/2015Classes/modules, itérateurs, arrow functions, tableaux typés, collections, promesses, améliorations maths, réflexion et proxies
7 / ES201606/2016Amélioration de la prise en compte d'ES2015, ajout de l'exponentiation ** et de la fonction Array.prototype.includes
8 / ES201706/2017Asychronisme avec await/async
9 / ES201806/2018Rest/Spread properties, amélioration des RegExp
ES201906/2019Array flattening, Object.fromEntries, amélioration JSON
.........
ES202506/2025Interator Helpers, Promise.try(), import modules JSON, etc.

Chaque proposition d'amélioration est discutée par le comité technique TC39 et progressivement validée
https://www.ecma-international.org/publications/standards/Ecma-262.htm.

Pour plus de détails sur le processus d'évolution du standard et l'histoire de Javascript :
https://www.youtube.com/watch?v=gytOcNFV1dM

Javascript, un langage de script interprété

Parmi tous les nombreux moteurs interprétant Javascript, on retiendra les trois principaux :

  • SpiderMonkey - Firefox, Gecko Layout Engine, Adobe Acrobat
  • V8 - Chrome, Node.js, Opera, MarkLogic
  • JavaScriptCore - Webkit, Safari, Qt5

Tous 3 supportent généralement la dernière spécification ECMA-262. D'autres (comme Chakra pour MS Edge Legacy - avant 2019) bloquent à des versions antérieures d'ES et ne proposent éventuellement que certaines des fonctionnalités récentes.

A noter que pour des questions de performances, ces moteurs utilisent la technique de compilation Just-In-Time, qui consiste à compiler à la voler le programme plutôt qu'avant son exécution (compromis entre compilation ahead-of-time et interprétation).
https://www.youtube.com/watch?v=p-iiEDtpy6I

Mauvaises nouvelles : le standard évolue régulièrement, et même si les navigateurs suivent, les utilisateurs ne les mettent pas forcément à jour systématiquement.

Plus de détails : https://en.wikipedia.org/wiki/List_of_ECMAScript_engines

Syntaxe et types de données
Plan du chapitre La syntaxe de Javascript

On retrouve en Javascript les ingrédients principaux en terme de syntaxe du langage Java :

  • des instructions qui se terminent par ;
  • des commentaires sur une ligne avec // ou sur (potentiellement) plusieurs /* ... */
  • des blocs de code délimités par { ... }
  • les structures de contrôle classiques if, while, for, etc.
Les variables (première approche)

En Javascript, les variables servent à stocker des valeurs. Dans une portée donnée, toutes les variables doivent être uniques.

InstructionDescription
var x = 42; déclare une variable nommée x qui a pour valeur initiale 42
var y; déclare une variable qui n'a pas encore de valeur (valeur undefined)
y = "42"; assigne à la variable y (déclarée précédemment) la chaîne "42"
var z = 42, t = y; permet de déclarer deux variables (z et t) et de leur affecter leurs valeurs initiales
(resp. 42 et la valeur de la variable y définie précédemment)

Pour l'instant, on déclare les variables avec var (notation historique), mais il existe aussi let et const dont on reparlera un peu plus tard...

Comme en Java, le nommage des variables est régi par les règles suivantes :

  • les noms de variables contiennent des lettres, des chiffres, _, $ et ne doivent pas commencer par un chiffre
  • les noms de variables sont sensibles à la casse : x et X sont deux noms différents
  • les mot-clés du langage Javascript ne sont pas autorisés
Types de données Plan du chapitre Les types de données
  • number - tous les nombres entiers ou flottants : 42, 1.0, -42e3
  • boolean - valeurs booléenne : true, false, 42 > "5"
  • string - chaînes de caractères délimitées par des guillemets simples ou doubles : "Quarante-Deux", 'La réponse à la grande question sur la vie, l\'univers et tout le reste.', "42"
  • object - type agrégé avec prototype : {prenom: "Fred", nombre: 42}
  • date - type Date avec fonctions de manipulation Date()
  • array - tableaux : [], [42, "quarante-deux"]
  • function - une fonction, first-class citizen en Javascript : function(...) { ... }
  • undefined - type d'une variable non définie
Quelques constantes remarquables
  • true, false - valeurs booléennes
  • Infinity - l'infini
  • NaN - not a number
  • undefined - absence de valeur (cas d'une variable déclarée, mais non définie/affectée)
  • null - valeur vide (mais valeur quand même)
Les ennuis commencent...

Javascript est faiblement typé, cela signifie que :

  • on ne définit pas le type d'une variable lorsque l'on déclare celle-ci :
    var x = 42;
  • le type d'une variable peut changer par une affectation :
    x = "Quarante-Deux";
  • des conversions de type au sein des expressions peuvent avoir lieu (en fonction des opérateurs), mais sans impact sur le type initial de la variable :
    var y = 40 + 2 * x; va nécessiter la conversion de la valeur de x calculer y (type casting/conversion/coercion).
Conversions de type
Valeur booléenneEn nombreEn chaîne
false0"false"
true1"true"
Valeur numériqueEn chaîneEn booléen
0"0"false
1"1"true
42"42"true
0.0"0.0"false
NaN"NaN"false
Infinity"Infinity"true
-Infinity"-Infinity"true
Conversions de type
ValeurEn nombreEn booléen
"0"0true (!)
"000"0true (!)
"42"42true
""0 (!)false (!)
"quarante-deux"NaNtrue

On peut se dire que la valeur booléenne d'une chaîne s correspond à s.length > 0.

Conversions de type
ValeurEn nombreEn chaîneEn booléen
[]0 (!)""true
[42]42 (!)"42"true
[40,2]NaN"40,2"true
["quarante"]NaN"quarante"true
["quarante",2]NaN"quarante,2"true
Conversions de type
ValeurEn nombreEn chaîneEn booléen
Conversions d'objets :
{}NaN"[object Object]"true
Conversions de fonctions :
function(...){...}NaN"function(...){...}"true
Conversions de valeurs remarquables :
null0 (!)"null"false
undefinedNaN"undefined"false
Opérateurs Opérateurs de conversion de type
Number("42")42
Number(false)0
String(42)"42"
String([42,"quarante-deux"])"42,quarante-deux"
"4.2" * 1042
"42" + 40 + 2"42402"
40 + 2 + "42""4242"
"42" < 43true
Opérateurs pour connaître les types

typeof permet de connaître le type d'une valeur. Cette fonction renvoie une chaîne de caractères.

typeof "42""string"
typeof 42"number"
typeof 42 == "42""boolean"
typeof {}"object"
typeof {x: 42}"object"
typeof [42]"object" (!)
typeof new Date()"object" (!)
typeof function(...){...}"function"
typeof null"object"
typeof undefined"undefined"

La propriété constructor appliqué à n'importe quelle donnée permet de connaître un peu plus précisément son type.

"42".constructorfunction String() {...}
42.constructorfunction Number() {...}
true.constructorfunction Boolean() {...}
{}.constructorfunction Object() {...}
[42].constructorfunction Array() {...} (!)
new Date().constructorfunction Date() {...} (!)
function(...){...}.constructorfunction Function() {...}
Opérateurs classiques

On retrouve les opérateurs arithmétiques qui opèrent entre deux nombres :

OpérateurExemple
+40 + 242
-45 - 342
*6 * 742
/255 / 642.5
%255 % 63

On retrouve les opérateurs classique d'affectation :

OpérateurExempleEquivalent à
=x = yx = y
+=x += 42x = x + 42
-=x -= 42x = x - 42
*=x *= 42x = x * 42
/=x /= 42x = x / 42
%=x %= 42x = x % 42
++x++x = x + 1
--x--x = x - 1
Opérateurs de comparaison

Comparaison des valeurs (avec conversion de type)

OpérateurExempleValeur
==42 == 42true
42 == 40false
42 == "42"true
null == undefinedtrue
!=42 != 42false
42 != 40true
42 != "40"true

Comparaison des types et des valeurs

OpérateurExempleValeur
===42 === 42true
42 === 40false
42 === "42"false
null === undefinedfalse
!==42 !== 42false
42 !== 40true
42 !== "42"true
Opérateurs de comparaison
OpérateurExempleValeur
>=42 >= 40true
40 >= 42false
"abc" >= "ABC"true
42 >= "40"false
true >= 0true
[4] < 42true
"d" >= function(){}false
>42 > "40"false
<=42 <= 40true
<42 < "40a"false

Ces opérateurs s'appliquent entre chaînes ou entre nombres. Les données sont automatiquement converties vers l'un ou l'autre de ces types.

Opérateurs booléens

Opérateurs booléens

OpérateurExempleValeur
&&true && falsefalse
||true || falsetrue
!!"42"false

Tester le type d'un objet

OpérateurExempleValeur
instanceof[] instanceof Arraytrue
[] instanceof Objecttrue

Opérateurs bit-à-bit (sur nombres 32 bits)

OpérateurExempleValeur
&5 & 20
|5 | 27
~~ 5-6
^5 ^ 27
<<5 << 220
>>-5 >> 2-2
>>>-5 >>> 21073741822

Ces opérateurs peuvent être également être utilisés
dans des affectations (ex. x >>= 2).

Précédence des opérateurs
NiveauOpérateurSignificationExemple
19(...)Groupement(40+2)
18.accès à une propriétépoint.x
[]accès à une propriétépoint["x"]
17()appel de fonctioncalcul()
newcréation d'objetnew Point()
16++incrément postfixéi++
--décrément postfixéi--
15++incrément préfixé++i
--décrément préfixé--i
!non logique!(42 == "42")
typeoftype d'une donnéetypeof 42

Source : https://www.w3schools.com/js/js_arithmetic.asp

Précédence des opérateurs
NiveauOpérateurSignificationExemple
14*multiplication6 * 7
/division21 / 0.5
%modulo85 % 43
**exponentiel2 ** 16
13+addition40 + 2
-soustraction45 - 3
12<<décalage vers la gauchex << 2
>>décalage vers la droitex >> 2
>>>décalage vers la droite non signéx >>> 2

Source : https://www.w3schools.com/js/js_arithmetic.asp

Précédence des opérateurs
NiveauOpérateurSignificationExemple
11<inférieur6 < 7
<=inférieur ou égal6 <= 7
>=supérieur6 > 7
>=supérieur ou égal6 <= 7
10==égalité42 == "42"
===égalité stricte42 === "42"
!=inégalité42 != "40"
!==inégalité stricte42 !== "42"
6&&et logiquetrue && false
5||ou logiquetrue || false
NiveauOpérateurSignificationExemple
3=affectationx = 42
+=affectationx += 42
-=affectationx -= 42
*=affectationx *= 42
/=affectationx /= 42
%=affectationx %= 42
<<=affectationx <<= 2
>>=affectationx >>= 2
>>>=affectationx >>>= 2
&=affectationx &= 2
^=affectationx ^= 2
|=affectationx |= 2
Structures de contrôle Plan du chapitre Les structures de contrôle
DescriptionSyntaxe
Blocs de code{ ... }
une seule instruction
Alternativesif (...) ... else ...
... ? ... : ...
switch (...) { case ... }
Boucleswhile (...) ...
do ... while (...)
for (... ; ... ; ...) ...
  • for ... in
  • for ... of
  • throw
  • try ... catch ... finally
  • continue
  • break
Structures de contrôle peu conventionnelles
Structures de contrôle peu conventionnelles
Structures de contrôle peu conventionnelles
Structures de contrôle peu conventionnelles
Structures de contrôle peu conventionnelles
Structures de contrôle peu conventionnelles
Objets standards de Javascript Plan du chapitre Les nombres Les nombres en Javascript
  • Javascript ne propose qu'un seul type nombre pour les valeurs numériques.
  • Différentes écritures sont possibles : 42 (sans décimales), 4.2 (avec décimales), 4.2e1 ou 0.42e-2 (exposant pour les grands nombres).
  • Les nombres sont tous représentés par des virgules flottantes en 64 bits : 1 bit de signe (bit 63), 11 bits pour l'exposant (bits 62-52) et les 52 bits restants pour la mantisse.
  • Jusqu'à 15 chiffres, les entiers sont précis. Au delà, il y a quelques arrondis.
  • Bad news, les calcul avec des nombres flottants en représentation IEEE-754, c'est pas top...
    0.1 + 0.20.30000000000000004 -- solution : réécrire en (0.1*10 + 0.2*10)/10

Plus de détails : https://www.w3schools.com/js/js_numbers.asp ou le cours d'architecture des ordinateurs de L2

Les nombres en Javascript

Javascript utilise l'opérateur + pour l'addition et la concaténation.

ExpressionRésultat
10 + 2030
"10" + "20""1020"
10 + "20""1020"
"10" + 20"1020"
"Le résultat est " + 10 + 20"Le résultat est 1020"
10 + 20 + "30""3030"

Javascript fonctionne de gauche à droite.

Les nombres en Javascript

Pour les autres opérations de calcul, les chaînes sont converties en nombre...quand c'est possible.

ExpressionRésultat
"100" / "10"10
"100" * "10"1000
"100" - "10"90
100 / "babar"NaN

La valeur NaN peut rapidement apparaître et se propager dans les calculs.

ExpressionRésultat
5 + NaNNaN
NaN + "5""NaN5"
typeof NaN"number"

Il n'y a pas d'erreur de division par 0...mais est-ce un bien ?

Toutes les subtilités de la coercion de type en Javascript :
https://dev.to/promhize/what-you-need-to-know-about-javascripts-implicit-coercion-e23

Les nombres en Javascript

La valeur Infinity (ou -Infinity) désigne un nombre trop grand pour être représenté.

ExpressionRésultat
2 / 0Infinity
-2 / 0-Infinity
typeof Infinity"number"

Par défaut, les nombres sont en décimal. Il est possible de les écrire en hexadécimal en les préfixant par 0x (ex. 0x2A).

On peut utiliser la méthode toString(base) pour afficher le nombre dans la base souhaitée.

ExpressionRésultat
var x = 42;
x.toString(16)"2a"
x.toString(8)"52"
x.toString(2)"101010"
La classe Number

Les nombres peuvent aussi se représenter comme des objets.

var l = 42; (littéral -- typeof l"number")
var o = new Number(42) (objet -- typeof o"object")

ExpressionRésultat
42 == new Number(42)true
42 === new Number(42)false
new Number(42) == new Number(42)false
new Number(42) === new Number(42)false
Number(42) === Number(42)true

Il est fortement déconseillé de créer des objets Number (temps de calcul, coût mémoire).

La classe Number
PropriétéDescription
MAX_VALUEValeur maximale possible (1.7976931348623157e+308)
MIN_VALUEValeur minimale possible (5e-324)
POSITIVE_INFINITYInfini positif (Infinity)
NEGATIVE_INFINITYInfini négatif (-Infinity)
NaNValeur Not-a-Number
La classe Number

Méthodes statiques (pas besoin de créer d'objet pour les invoquer) :

MéthodeDescriptionExemple
isFinite(n)Vérifie si n est un nombre finiNumber.isFinite(42)→ true
Number.isFinite(Infinity)→ false
isInteger(n)Vérifie si n est un nombre entierNumber.isInteger(42)→ true
Number.isFinite(4.2)→ false
isNaN(n)Vérifie si n n'est pas un nombreNumber.isNaN(42)→ false
Number.isNaN(42/"babar")→ true
isSafeInteger(n)Vérifie si n est un entier qui peut être représenté par un réel double précision selon IEEE 754Number.isSafeInteger(42)→ true
Number.isSafeInteger(Math.pow(2,53)-1)→ true
Number.isSafeInteger(Math.pow(2,53))→ false
Number.isSafeInteger("42")→ false
La classe Number

Méthodes d'instance (qui s'invoquent sur un nombre variable ou objet) :

MéthodeDescriptionExemple
toExponential(x)retourne le nombre en notation exponentielle
x (optionnel) donne le nombre de digits (0 à 20)
var n = 42;
n.toExponential(); → "4.2e1"
toFixed(x)retourne le nombre avec x décimalesvar n = 0.49;
n.toFixed(1); → "0.5"
toPrecision(x)retourne le nombre écrit avec x digitsvar n = 0.49;
n.toPrecision(3); → "0.490"
La classe Math

La principale propriété est la constante Math.PI. La classe Math propose également (entre autres) le nombre d'Euler (Math.E), les logarithmes néperiens de 2 (Math.LN2), la racine de 2 (Math.SQRT2) etc.

  • fonctions classiques : valeur absolue (Math.abs(x)), exponentielle (Math.exp(x)), logarithme (Math.log(x)), puissance (Math.pow(x,y)), et racine carrée (Math.sqrt(x))
  • fonctions trigonométriques (avec angles en radians) : cosinus (Math.cos(x)), sinus (Math.sin(x)), tangeante (Math.tan(x)), etc.
  • fonctions d'arrondis : à l'entier inférieur (Math.floor(x)), à l'entier supérieur (Math.ceil(x)), à l'entier le plus proche (Math.round(x))
  • fonctions utilitaires : minimum d'une liste de valeurs (Math.min(a,b,...)), maxium (Math.max(a,b,...)) génération aléatoire (Math.random() qui renvoie un réel entre 0 inclus et 1 exclu)

Plus de détails sur l'objet Math : https://www.w3schools.com/jsref/jsref_obj_math.asp

Les chaînes de caractères Les chaînes de caractères

Les chaînes de caractères sont une suite de caractères délimités par des guillemets simples (ex. 'Fred') ou doubles (ex. "Fred"). Contrairement à PHP le choix de l'un ou l'autre des délimiteurs ne fait pas de différence.

La longueur d'une chaîne est donnée par la propriété length (sans les parenthèses !) - Exemple : "Fred".length4

Classiquement, les indices des chaînes commencent à 0.

Les méthodes les plus utilisées sont similaires à celles proposées par Java.

MéthodeDescriptionExemple
charAt(i)Renvoie le caractère à l'indice i"Fred".charAt(1) → "r"
indexOf(c)Renvoie l'indice de la première occurrence de c"Frederic".indexOf("r") → 1
lastIndexOf(c)Renvoie l'indice de la dernière occurrence de c"Frederic".lastIndexOf("r") → 5
startsWith(c)Teste si la chaîne commence par c"Fred".startsWith("Fr") → true
endsWith(c)Teste si la chaîne termine par c"Fred".endsWith("d ") → false
includes(s,i)Teste si la sous-chaîne s est présente depuis l'indice i"Frederic".includes("er") → true
Les chaînes de caractères
MéthodeDescriptionExemple
substring(i1,i2)Extrait la sous-chaîne entre i1 (inclus)
et i2 (exclus)
"Frederic".substring(2,4) → "ed"
substr(i,l)Extrait la sous-chaîne de longueur l
commençant à i
"Frederic".substr(2,4) → "eder"
trim()Enlève les espaces au début et en fin de chaîne" Fred ".trim() → "Fred"
concat(s)Concatène à la chaîne s"Fred".concat("eric") → "Frederic"
repeat(n)Répète la chaîne n fois"Fred".repeat(2) → "FredFred"
toUpperCase()Mise en majuscule des lettres"Fred!".toUpperCase() → "FRED!"
toLowerCase()Mise en minuscule des lettres"Fred!".toLowerCase() → "fred!"
split(d)Découpage d'une chaîne par rapport
au délimiteur d
"Frederic".split("e") → ["Fr","d","ric"]
"Fred".split("") → ["F","r","e","d"]

Toutes les méthodes retournent une nouvelle chaîne.

Les gabarits de chaîne

Introduits par ES2015, cette construction (également appelée littéraux de gabarits) permet d'intégrer à une chaîne de caractères des expressions.

Les littéraux de gabarits sont délimités par des ` (accent grave) au lieu des habituels guillemets simples ou doubles.

Il est possible de faire apparaître des expressions délimitées par ${...} au sein de la chaîne et qui seront évaluées et substituées pour construire la chaîne. Par ailleurs, il est possible de déclarer des chaînes multi-lignes.

Cette utilisation est la plus courante des littéraux de gabarit, mais nous reviendrons plus tard sur d'autres types.

Les expressions régulières

Certaines fonctions sur les chaînes fonctionnent avec des expressions régulières. Celles-ci permettent de décrire des ensembles de chaînes de caractères, reconnues (matchées) par ces expressions régulières.

Une expression régulière est composée de deux éléments : un pattern et un modifie(u)r sous le format :
var patt = /pattern/modifier

On dénote 3 modifieurs :

ModifieurDescription
iRéalise un pattern-matching insensible à la casse.
gRéalise un pattern-matching global, qui ne s'arrête pas à la première sous-chaîne reconnue.
mRéalise un pattern-matching multi-lignes, qui prend en compte les retours à la ligne.
vRéalise un pattern-matching compatible UTF-16.
Les expressions régulières

La notation avec [ ] ou ( | ) permet de liste des caractères/chaînes/sous-chaîne autorisées.

NotationDescription
[abc]Trouve un des caractères entre les crochets.
[^abc]Trouve un des caractères qui n'est pas entre les crochets.
[a-zA-Z]Trouve un des caractères dans les intervalles entre crochets.
[^a-zA-Z]Trouve un des caractères qui n'est pas dans les intervalles entre crochets.
(a|b|c)Trouve un des caractères séparés par des |.
Les expressions régulières

Une partie de schéma peut-être répétée en utilisant des quantificateurs.

NotationDescription
n+Reconnaît une chaîne contenant au moins une occurrence de n.
n*Reconnaît une chaîne contenant 0 ou plusieurs occurrences de n.
n?Reconnaît une chaîne contenant 0 ou une occurrence de n.
n{X,Y}Reconnaît une chaîne contenant entre X et Y répétitions de n.
n{X}Reconnaît une chaîne contenant X répétitions de n.
n{X,}Reconnaît une chaîne contenant au moins X répétitions de n.
^nReconnaît une chaîne qui commence par n.
n$Reconnaît une chaîne qui termine par n.
Les expressions régulières

Plutôt que d'énumerer les caractères autorisés/interdits individuellement, on peut utiliser des meta-caractères.

NotationDescription
.N'importe quel caractère (excepté les retours à la ligne).
\w / \WN'importe quel caractère d'un mot/qui n'est pas d'un mot.
\d / \DN'importe quel chiffre/caractère qui n'est pas un chiffre.
\s / \SN'importe quel caractère d'espacement/qui n'est pas un espacement.
\b / \BReconnaît une sous-chaîne qui est/n'est pas au début ou à la fin d'un mot.
\0Le caractère NUL.
\nLe caractère de retour à la ligne.
\tLe caractère de tabulation.
\rLe caractère de retour chariot.
...
Les expressions régulières
Description Expression régulière
Nom d'utilisateur /^[a-z0-9_-]{3,16}$/i
Mot de passe /^[a-z0-9_-]{6,18}$/i
Valeur hexadécimale /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i
Adresse email /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/i
URL /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/i
Adresse IP /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i
Retour aux chaînes de caractères
MéthodeDescription
search(regexp) Renvoie l'indice de la première occurrence de regexp.
match(regexp) Renvoie un tableau des sous-chaînes reconnues par l'expression régulière, null si aucune n'est reconnue.
replace(regexp,str) Replace les occurrences de regexp par la chaîne str.
replaceAll(regexp,str) Replace toutes les occurrences de regexp par la chaîne str (ES2020).
Les dates Le type Date

Contrairement aux nombres et aux chaînes, les dates sont nécessairement encapsulées dans un objet Date.

Une date est représentée par un jour, un mois, une année, une heure, des minutes, des secondes et des millisecondes.

ConstructeurDescription
new Date()Construit la date courante.
new Date(millisecondes)Construit une date correspondant au nombre de millisecondes écoulées depuis le 1er janvier 1970 à minuit (temps zéro).
new Date(chaine) Construit une date correspondant à la chaîne
new Date(année, mois, jour, heure, min, sec, millisec) Construit une date correspondant à jour/mois+1/année heure:min:sec (le mois de janvier vaut 0).

Pour plus de détails sur les formats de chaînes de caractères associés aux dates :
https://www.w3schools.com/js/js_date_formats.asp

Le type Date
MéthodeDescription
getDate()/setDate(d)Retourne/spécifie le jour du mois (1-31).
getDay()/setDay(d)Retourne/spécifie le jour de la semaine (0-6).
getFullYear()/setFullYear(d)Retourne/spécifie l'année.
getHours()/setHours(d)Retourne/spécifie l'heure (0-23).
getMilliseconds()/setMilliseconds(d)Retourne/spécifie les millisecondes.
getMinutes()/setMinutes(d)Retourne/spécifie les minutes (0-59).
getMonth()/setMonth(d)Retourne/spécifie le mois (0-11).
getSeconds()/setSeconds(d)Retourne/spécifie les secondes (0-59).
getTime()/setTime(d)Retourne/spécifie le nombre de secondes depuis "temps zéro".

Une variante de ces méthodes existe avec UTC (ex. getUTCTime()) pour spécifier la date par rapport à UTC. Par défaut les dates manipulées correspondent à la locale du poste client.

Le type Date
MéthodeDescription
now()Retourne le nombre de secondes depuis le temps zéro (méthode statique)
Usage : Date.now()
parse(chaine)Extrait une date de la chaîne de caractères
(ex. Date.parse("January, 8th 2017")).
getTimezoneOffset()Retourne la différence de temps entre le temps local et UTC, en minutes.
(ex. pour la France : 60).
toLocaleDateString()Retourne la partie date d'un objet Date en fonction de la zone.
(ex. pour la France : "2017-12-1").
toLocaleTimeString()Retourne la partie horaire d'un objet Date en fonction de la zone.
(ex. pour la France : "15:48:32").
toString()Retourne une chaîne représentant la date courante
(ex. "Fri Dec 01 2017 15:41:50 GMT+0100 (CET)").
toISOString()Retourne une chaine représentant la date courante au format ISO-8601
(ex. "2017-12-01T14:44:14.195Z")
Fonctions et propriétés globales Fonctions et propriétés globales

Javascript propose quelques fonctions et propriétés globales qui n'appartiennent officiellement pas à un objet spécifique (en réalité, si : ce sont des propriétés et des méthodes proposées par un objet global qui appartient à l'environnement dans lequel s'exécute le moteur -- par ex. l'objet window du navigateur).

ConstanteDescription
InfinityValeur numérique représentant l'infini (positif/négatif)
NaNValeur "Not-a-Number"
undefinedIndique qu'une variable n'a pas été assignée.
Fonctions et propriétés globales
FonctionDescriptionExemple
String(x)Calcule la valeur de x comme chaîne.String([]) → ""
Number(x)Calcule la valeur numérique de x.Number([]) → 0
Boolean(x)Calcule la valeur booléenne de x.Boolean([]) → true
isNaN(x)Teste si x n'est pas un nombre.isNaN(2/"babar") → true
isFinite(x)Teste si x est un nombre fini.isFinite(Infinity) → false
parseInt(x)Extrait un entier de la chaîne x.parseInt("42 is ze best") → 42
parseInt("j'aime 42") → NaN
parseFloat(x)Extrait un réel flottant de la chaîne x.parseFloat("42.5 isn't ze best") → 42.5
Fonctions et propriétés globales
FonctionDescription
encodeURI(x)Encode l'url x en remplaçant les caractères spéciaux (sauf , / ? : @ & = + $ #).
encodeURIComponent(x)Encode l'url x en remplaçant les caractères spéciaux (y compris , / ? : @ & = + $ #).
decodeURI(x)Decode l'url x.
decodeURIComponent(x)Decode l'url x.
eval(code)Evalue le code Javascript représenté par la chaîne code.
Les fonctions
Plan du chapitre Généralités Les fonctions

Les fonctions :

  • permettent de factoriser du code, en vue de faciliter l'écriture et la maintenance du code,
  • peuvent admettre des paramètres
  • peuvent renvoyer une valeur

En Javascript, le typage est dynamique, de ce fait, quand on déclare une fonction :

  • on ne lui déclare pas de type de retour
  • les paramètres ne sont pas typés, il n'y a pas de vérification de type sur les paramètres

Et en plus, on peut appeler la fonction sans renseigner tous ses paramètres.

Un premier exemple de fonction function nomFonction(argument1,argument2,...) {
   ... // code de la fonction
}
Les paramètres Les paramètres de fonctions

Si un appel de fonction ne présente pas assez de paramètres par rapport à la signature déclarée, les valeurs manquantes sont undefined.

Si un appel de fonction propose trop de valeurs de paramètres, les valeurs en trop sont ignorées.

Les paramètres de fonctions

Classiquement, la valeur retournée par la fonction est renvoyée par return.

Si la fonction ne renvoie aucune valeur, alors sa valeur de retour est undefined.

Les paramètres

Chaque fonction présente un objet arguments qui est un tableau permettant d'accéder aux arguments de la fonction appelée et permet de connaître leur nombre, valeurs, etc.

Les paramètres

Il est possible d'utiliser la notation ... pour désigner un nombre d'arguments quelconque. Cette notation (appelée Rest) ne s'applique, si nécessaire, qu'au dernier paramètre. Les arguments sont ensuite accédés comme un tableau.

Les paramètres

A l'inverse, il est également possible d'utiliser la notation ... (appelée cette fois-ci Spread) sur un tableau lors d'un appel de fonction. Les valeurs du tableau seront alors utilisées comme les différents arguments de la fonction.

Les paramètres

Les arguments sont passés par valeur. La fonction n'a accès qu'aux valeurs, pas à l'emplacement mémoire des arguments. De ce fait, si la valeur d'un argument est changée par la fonction, cela n'affecte pas la variable initiale.

Les paramètres

Les références sont les valeurs des objets. Si la fonction modifie la valeur d'attribut d'un objet en paramètre, celui-ci restera modifié.

Portée des variables Les portées des variables
  • Portée globale : pour les variables déclarées en dehors de toute fonction, qui seront accessibles de n'importe où (ie. dans n'importe quelle fonction ou en dehors), mais aussi les variables affectées dans une fonction sans avoir été déclarées (!)
  • Portée locale : pour les variables déclarées dans une fonction, qui ne seront accessible que depuis la fonction où elles sont déclarées.
Les portées des variables

Pour pallier au problème de portée des variables déclarées avec var, ES2017 a introduit deux mots-clés pour définir des données :

  • const qui permet de définir une constante. Celle-ci constante devra obligatoirement être déclarée avec une valeur assignée.
  • let qui permet de déclarer une variable locale au bloc de code considéré.
Déclaration vs. Expressions

La notation vue précédemment function f(...) { ... } est une déclaration de fonction. On peut aussi définir une fonction comme une expression qui sera stockée dans une variable.

var f = function(arguments...) { /* ... code de la fonction ... */ }

On déclare ainsi une fonction anonyme qui sera appelée par l'intermédiaire de la variable dans laquelle elle est enregistrée.

Notation alternative

ES2015 a introduit les fonctions fléchées (arrow functions). Il s'agit d'un sucre syntaxique [avec une petite subtilité, on y reviendra plus tard] pour la déclaration d'une fonction dans le plus pure style de la programmation fonctionnelle à la Caml.

Au lieu d'écrire :on pourra écrire :
function (a, b) {
  return a + b;
}
(a, b) => { return a + b; }
function (a, b) {
  return a + b;
}
(a, b) => a + b;
S'il n'y a qu'une instruction on économise le return
function (a) {
  return a * a;
}
a => a * a;
S'il n'y a qu'un seul paramètre, pas besoin de (...)

Voir https://medium.freecodecamp.org/arrow-functions-in-javascript-2f8bf7df5077 pour plus de détails sur l'utilité de cette notation.

Un premier bilan sur les fonctions
  • Les fonctions peuvent être stockées dans des variables, si déclarées comme des expressions. Dans ce cas, il s'agit de fonctions anonymes.
  • Les fonctions peuvent être utilisées comme des valeurs, qui sont passées en paramètre, ou renvoyées par une fonction.
Un premier bilan sur les fonctions

Functional programming in Javascript : https://www.youtube.com/watch?v=e-5obm1G_FY

Déclaration vs. Expressions

En Javascript, il existe un mécanisme qui s'applique automatiquement sur le code et qui va remonter les déclarations (fonctions ou variables) en début de script ou de fonction.

De ce fait, les déclarations de fonctions sont remontées, tandis que seule la déclaration de la variable contenant la fonction est remontée, mais pas son initialisation.

Hoisting et closures Le hoisting en Javascript

Le hoisting est un principe de Javascript qui consiste à faire remonter automatiquement les déclarations (fonctions ou variables) au début du script ou de la fonction correspondante.

Attention, le hoisting a pour effet de ne faire remonter que la déclaration, et pas l'initialisation.

Le hoisting en Javascript

Le hoisting s'applique aux fonctions et variables déclarées avec var, mais pas à celles déclarées avec let ou const... enfin pas exactement.

Les déclarations sont (en interne) effectivement bien remontées, mais une donnée déclarée avec let ou const n'est pas accessible avant sa déclaration, sous peine de générer une erreur.

Les closures

En Javascript une closure est une fonction (anonyme ou non) qui va accéder aux variables de l'environnement dans lequel elle est définie et les utiliser/modifier.

Les closures

Une closure ("fermeture") est appelée ainsi car elle enferme avec elle les variables qui se trouvaient dans l'environnement où elle est définie. Lorsqu'elle s'exécute, les variables sont accédées et utilisées comme si elles étaient globales, pourtant ces variables restent locales à la fonction dans laquelle elles étaient déclarées.

Curryfication en Javascript

La curryfication est un processus qui consiste à casser une fonction à plusieurs arguments pour en faire une suite d'appels de fonctions avec chacun des arguments successifs (nom donné d'après Haskell Brooks Curry). En Javascript, les closures permettent de réaliser ce genre de procédé.

Pour plus d'informations sur ce concept (un peu abstrait) : https://fr.wikipedia.org/wiki/Curryfication

Retour sur les gabarits de chaîne

Les gabarits de chaîne peuvent aussi contenir des étiquettes grâce à la syntaxe etiquette`...`. On parle alors de gabarits étiquetés.

Dans ce cas l'étiquette est une fonction function(chaines, ...expressions) où le premier paramètre est un tableau des différentes sous-chaines "fixes" constituant le gabarit, et les paramètres restants sont les expressions contenues dans la chaîne.

Fonctions immédiatement invoquées (IIEF)

Cette pratique consiste à déclarer une fonction qui s'exécute immédiatement. L'intérêt principal est de créer une portée locale dont les variables ne viendront pas "polluer" le scope global.

Fonctions immédiatement invoquées (IIEF)
  • Les déclaration de fonctions ne peuvent pas être écrites sous cette forme.
  • Si la fonction retourne un résultat, celui-ci peut bien sûr être récupéré.
Les tableaux
Généralités Plan du chapitre Les tableaux

Les tableaux sont des structures de données classiques qui permettent de stocker plusieurs valeurs.

En Javascript, les tableaux sont toujours indicés par des entiers. Le contenu n'est pas obligatoirement homogène.

Pour déclarer un tableau, on utilise la syntaxe suivante :

var profsDuS6 = ["jube", "fred", "jmh", 42];

Il existe aussi une autre syntaxe pour faire la même chose :

var profsDuS6 = new Array("jube", "fred", "jmh", 42);

Toutefois, bien que les tableaux (tout comme les chaînes) soient considérés comme des objets, il n'est pas nécessaire de les instancier avec un new pour des questions de performances.

A noter que new Array(10) créé un tableau vide de taille 10, mais new Array("10") créé un tableau contenant le seul élément "10".

Les tableaux

Pour accéder au contenu d'un tableau, on utilise la notation [indice] classique.
L'indice est forcément une expression de type entier (ou qui sera convertie en entier pour être évaluée).

La longueur d'un tableau est donnée par sa propriété length.

Les tableaux

L'utilisation de typeof ne va pas nous aider : typeof []"object".

Trois solutions :

  1. Utiliser Array.isArray(t), mais celui-ci n'est supporté que depuis ECMAScript 5 (pas sur les anciens navigateurs).
  2. Tester le constructeur : t.constructor.toString().indexOf("Array") > -1 ... un peu tordu non ?
  3. Utiliser t instanceof Array : la meilleure solution
Les tableaux

Plusieurs solutions existent, exemple avec t = ["fred", "jube"] :

  • Utiliser l'indice : t[n], sachant que
    un indice inférieur strictement à la longueur écrase un élément existant : t[0] = "jmh"["jmh", "jube"]
    un indice supérieur strictement à la longueur crééra des vides : t[3] = "jmh"["fred", "jube", undefined, "jmh"]
  • Utiliser la méthode push(valeur) : t.push("jmh")["fred","jube","jmh"]
    Cette méthode équivaut à t[t.length] = "jmh"
  • Utiliser la méthode splice qui permet d'insérer des éléments à un indice spécifique en effaçant certains éléments : t.splice(1, 0, "jmh")["fred", "jmh", "jube"]
Méthodes utiles Les tableaux - méthodes utiles
FonctionDescription
t1.concat(t2, t3, ...) Renvoie la concaténation du tableau avec le(s) tableau(x) en paramètre.
var t1 = ["a"], t2 = ["b","c"], t3 = [42];
t1.concat(t2, t3)["a", "b", "c", 42]
t.copyWithin(i, d, f) Renvoie le tableau où on a remplacé les éléments à partir de l'indice i par une copie
des éléments entre les indices d (inclus) et f (exclu).
var t = ["a","b","c",42];
t.copyWithin(1, 2, 4)["a", "c", 42, 42]
t.fill(v, d, f) Remplit le tableau t avec la valeur v entre les indices optionnels d (inclus) et f (exclu).
var t = [1, 2, 3, 4, 5];
t.fill(42, 1, 3); → t == [1, 42, 42, 4, 5]
Les tableaux - méthodes utiles
FonctionDescription
t.indexOf(e,d) Renvoie l'indice de la première occurrence de e dans le tableau, en commençant à l'indice optionnel d.
var t = [1, 2, 4, 5, 3, 2, 1];
t.indexOf(2, 3) → 5
t.lastIndexOf(e,d) Renvoie l'indice de la dernière occurrence de e dans le tableau, en commençant à l'indice optionnel d.
var t = [1, 2, 4, 5, 3, 2, 1];
t.lastindexOf(1) → 6
t.includes(e) Teste sur l'élément existe dans le tableau [ES2016].
var t = [1, 2, 4, 5, 3, 2, 1];
t.includes(42) → false
Les tableaux - méthodes utiles
FonctionDescription
t.push(e1,e2,...) Ajoute en fin de tableau les éléments en paramètres et renvoie la nouvelle taille du tableau.
var t = ["fred","jube"];
t.push("jmh", "dermas") → 4
et t == ["fred","jube","jmh","dermas"]
t.unshift(e1,e2,...) Ajoute en début de tableau les éléments en paramètres et renvoie la taille du nouveau tableau.
var t = ["fred","jube"];
t.unshift("jmh", "dermas") → 4
et t == ["jmh","dermas",fred","jube"]
t.shift() Retire et retourne le premier élément du tableau.
var t = ["fred","jube","jmh"];
t.shift() → "fred"
et t == ["jube", "jmh"]
t.pop() Retire et renvoie le dernier élément du tableau.
var t = ["fred","jube"];
t.pop() → "jube"
et t == ["fred"]
Les tableaux - méthodes utiles
FonctionDescription
t.reverse() Inverse l'ordre des éléments du tableau et revoie celui-ci.
var t = ["fred","jube","jmh"];
t.reverse() → ["jmh", "jube", "fred"]
t.splice(i,n, e1, e2,...) Ajoute les éléments à l'indice i, et supprime à cet indice n éléments qui seront renvoyés par la fonction.
var t = ["fred","jube","jmh","dermas"]
t.splice(2,1,"karla"); → ["jmh"]
et t == ["fred","jube","karla","dermas"]
Les tableaux - méthodes utiles
FonctionDescription
t.join(sep) Renvoie une chaîne dans laquelle les éléments de t sont joints par le séparateur sep (optionnel - par défaut ,)
var t = [1, 2, 3, 4, 5];
t.join("-") → "1-2-3-4-5"
t.slice(d,f) Retourne un sous-tableau contenant les éléments du tableau entre l'indice d (inclus) et f (exclu).
var t = ["fred","jube","jmh","dermas"]
t.slice(1,3); → ["jube","jmh"]
Méthodes avec des fonctions en paramètre Les tableaux - méthodes utiles
FonctionDescription
t.every(f) Vérifie si tous les éléments du tableau vérifient la fonction en paramètre.
Signature de f : function(valeurCourante, indice, tableau)
Les deux derniers paramètres sont optionnels.
Les tableaux - méthodes utiles
FonctionDescription
t.filter(f) Extrait les éléments du tableau qui satisfont la fonction en paramètre. Ne modifie par le tableau d'origine.
Signature de f : function(valeurCourante, indice, tableau)
Les deux derniers paramètres sont optionnels.
Les tableaux - méthodes utiles
FonctionDescription
t.findIndex(f) Retourne l'indice du premier élément du tableau qui passe le test de la fonction.
Signature de f : function(valeurCourante, indice, tableau)
Les deux derniers paramètres sont optionnels.
t.findLastIndex(f) Idem, mais en partant de la fin du tableau (nouveauté ECMAScript 2023).
Les tableaux - méthodes utiles
FonctionDescription
t.forEach(f) Applique la fonction à chaque élément du tableau.
Signature de f : function(valeurCourante, indice, tableau)
Les deux derniers paramètres sont optionnels.
Les tableaux - méthodes utiles
FonctionDescription
t.map(f) Retourne un nouveau tableau composé des éléments du tableau d'origine sur lesquels on a appliqué la fonction.
Signature de f : function(valeurCourante, indice, tableau)
Les deux derniers paramètres sont optionnels.
Les tableaux - méthodes utiles
FonctionDescription
t.reduce(f, initiale) Réduit le tableau à une valeur en appliquant une fonction successivement sur tous les éléments du tableau, de gauche à droite.
Signature de f : function(accumulateur, valeurCourante, indice, tableau)
Les deux derniers paramètres sont optionnels.
Les tableaux - méthodes utiles
FonctionDescription
t.reduceRight(f, initiale) Réduit le tableau à une valeur en appliquant une fonction successivement sur tous les éléments du tableau, de droite à gauche.
Signature de f : function(accumulateur, valeurCourante, indice, tableau)
Les deux derniers paramètres sont optionnels.
Les tableaux - méthodes utiles
FonctionDescription
t.some(f) Vérifie si au moins un des éléments du tableau vérifie la fonction en paramètre.
Signature de f : function(valeurCourante, indice, tableau)
Les deux derniers paramètres sont optionnels.
Les tableaux - méthodes utiles
FonctionDescription
t.sort(f) Trie le tableau par rapport à la fonction de comparaison (optionnelle - si absente, comparaison classique).
Signature de f : function(valeur1, valeur2)
Renvoie : 0 si les deux valeurs sont égales, une valeur négative si valeur1 < valeur2, une valeur positive si valeur1 > valeur2
Les objets
Plan du chapitre Généralités La notion d'objet Les objets

Les objets représentent des entités du monde réel ou virtuel. Ils possèdent des propiétés qui permettent de caractériser leur état, et des méthodes qui permettent de le faire évoluer.

En Javascript, à part les types primitifs (entiers, chaine, booléen, null, undefined), tout le reste est objet : les objets bien sûr, mais aussi des objets spéciaux/prédéfinis comme Date, Maths, les expressions régulières, les tableaux et les fonctions.

A noter que Number, Boolean et String sont des objets (si créés avec new, mais on évitera de le faire).

En Javascript, les objets sont et peuvent être déclarés comme un ensemble de couples (nom, valeur).

La syntaxe pour déclarer un objet : var personnage = { nom: "Threepwood", prenom: "Guybrush" };

Les objets

En Javascript, les fonctions sont des valeurs de variables comme les autres. Les méthodes des objets sont donc des fonctions.

Comme dans tous les langages objet classiques, on retrouve le mot-clé this pour désigner l'objet sur lequelle la fonction est invoquée.

Les objets
  • L'accès aux propriétés d'un objet peut se faire soit avec la notation objet.propriete, soit avec la notation objet["propriete"].
  • L'accès aux méthodes d'un objet ne se fait que via la notation objet.methode(...) qui réalise l'invocation de la méthode sur l'objet. En l'absence des (...), objet.methode renvoie la fonction.
Les objets

Depuis ES2020, la notation obj?.prop1?.prop2 peut être utilisée pour accéder aux membres des objets sans avoir à vérifier si les objets conteneurs sont null ou undefined au préalable.

Les membres d'un objet Les objets

Il est possible d'ajouter directement une propriété à un objet, comme si on faisait une simple affectation :
obj.nouvellePropriete = valeur;

A noter qu'il est ainsi possible d'ajouter des propriétés à des objets natifs, mais cette pratique est à éviter.

Il est également possible de supprimer les propriétés d'un objet avec le mot-clé delete :
delete obj.proprieteASupprimer;

Les objets - les propriétés

Depuis ES2015, Javascript offre la possibilité d'utiliser des getters et des setters (des accesseurs) qui seront automatiquement appelés lorsque l'on utilise la notation objet.accesser lors d'une évaluation (get) ou d'une affectation (set).

Les objets - les propriétés

La classe Object propose quelques méthodes statiques intéressantes pour manipuler les objets. Notamment, defineProperty(obj, prop, desc) permet d'ajouter à l'objet obj la propriété prop avec un descripteur desc.

Suivant que la propriété est une donnée ou un accesseur, les propriétés suivantes sont possibles pour le descripteur :

  • configurable : booléen indiquant si le type de propriété peut être changé et si la propriété pourra être supprimée de l'objet.
  • enumerable : booléen indiquant si la propriété peut être listée dans une boucle for...in
  • writable : booléen indiquant si la propriété peut-être modifiée (descripteur de donnée)
  • value : la valeur par défaut, undefined si non renseigné (descripteur de donnée)
  • get : fonction utilisée comme getter, undefined si pas d'accesseur (descripteur d'accesseur)
  • set : fonction utilisée comme setter, undefined si non renseigné (descripteur d'accesseur)

Voir https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/defineProperty pour les details.

Les objets

La classe Object contient plusieurs méthodes statiques qui peuvent être utiles :

  • Object.keys(obj) renvoie un tableau des propriétés de l'objet en paramètre
  • Object.values(obj) renvoie un tableau des valeurs des propriétés de l'objet en paramètre
  • Object.entries(obj) renvoie un tableau de couples [propriete, valeur] de l'objet en paramètre
Les objets

La structure de boucle for...in permet de parcourir les attributs d'un objet.

Quelques concepts avancés Les objets - propriétés énumérables

Pour qu'une propriété apparaisse dans une itération for...in, elle doit être enumerable. Pour contrôler cette propriété sur un attribut, il faut passer par la méthode Object.defineProperty(...).

Les fonctions sont des objets

Les fonctions déclarées dans le code sont des objets Function qui possèdent notamment : un nom (name), une taille (length) représentant le nombre d'arguments. Ceci a deux conséquences :

  • assigner une fonction à une variable assigne la référence de l'objet Function correspondant, créant un alias pour la fonction d'origine
  • il est possible de leur assigner des propriétés ou des fonctions (cela ne semble pas très utile sur le moment, mais vous allez voir par la suite, c'est juste central).
La valeur this Les objets

Etant donné que ce sont des objets, les fonctions peuvent donc, dans leur code, faire référence à this, même si ces fonctions ne sont pas rattachées à un objet. Dans ce cas, elles référenceront l'objet global.

Les objets

Attention, contrairement à une fonction déclarée avec le mot-clé function, les fonctions fléchées ne sont pas liées à l'objet this de l'objet qui les contient. Pour elles, this représente l'objet qui a défini cette fonction (closure).

Les objets

Lors de leur appel, il sera possible de rattacher à ces fonctions l'objet à considérer comme this.

On utilise pour cela la méthde bind proposée par l'objet Function qui créé une fonction liée var boundF = f.bind(objet); qu'il ne restera plus qu'à invoquer ensuite : boundF(...).

De manière générale, bind permet aussi de fixer certains arguments de la fonction liée, pas uniquement this.

Les objets

Une autre solution consiste à s'appuyer sur la méthode apply également présente dans les fonctions.

La syntaxe est la suivante : f.apply(objetThis, tableauArguments).

Les objets

Une autre solution consiste à s'appuyer sur la méthode call également présente dans les fonctions.

La syntaxe est la suivante : f.call(objetThis, argument1, argument2, ...)

bind, apply et call permettent notamment de "voler" une méthode d'un objet pour l'invoquer sur un autre.

Les objets

Javascript propose des constructeurs intégrés pour les objets natifs (Object, String, Number, Boolean, Array, RegExp, Function, Date).

Bien que Javascript propose des objets pour les types natifs, il n'est pas nécessaire :

  • d'utiliser new Object() pour créer un objet, on utilisera plutôt le littéral {}
  • d'utiliser new String(...) pour créer une chaîne, on utilisera plutôt le littéral "..."
  • d'utiliser new Number(42) pour créer un nombre, on utilisera plutôt le nombre en littéral 42
  • d'utiliser new Boolean(false) pour créer un booléen, on utilisera plutôt le littéral false
  • d'utiliser new Array() pour créer un tableau, on utilisera plutôt le littéral []
  • d'utiliser new RegExp(...) pour créer une expression régulière, on utilisera plutôt /.../..
  • d'utiliser new Function(...) pour créer une fonction, on utilisera plutôt function(...){...}
Les prototypes Les objets - Construire des objets à partir d'un prototype

La notation {...} permet de construire un seul objet. Pour construire un ensemble d'objets contenant les mêmes propriétés, Javascript propose un mécanisme spécifique nommé prototype.

Cette notion permet de créer des objets qui "héritent" automatiquement des propriétés et fonctions d'un objet de référence, appelé... prototype.

Pour créer un objet à partir d'un prototype, on utilise la méthode Object.create(proto).

Fonctionnement Les objets - les prototypes

Il s'agit du mécanisme d'héritage de Javascript. Chaque objet a un prototype, et les prototypes sont eux-mêmes des objets. Les objets héritent des propriétés et des méthodes de leur prototype.

Un prototype est donc un objet qui sert de ressource pour fournir un ensemble de propriétés et de méthodes à un objet.

Pour accéder à une propriété ou à une fonction de l'objet courant, Javascript applique le principe suivante :

  • Si la propriété/fonction est dans l'objet, c'est elle qui est considérée.
  • Sinon, on regarde si la propriété/fonction est présente dans le prototype de l'objet. Si c'est le cas, c'est elle qui est considérée.
  • Si la propriété/fonction n'est pas dans le prototype, on recommence avec le prototype du prototype, et ainsi de suite.
  • Si toute la chaîne de prototype a été explorée et que la propriété/fonction n'a pas été trouvée, alors la valeur est undefined.
Tous les objets possèdent des prototypes

Au sommet de la hiérarchie, tous les objets héritent d'un prototype Object.prototype.

Par exemple, les objets Date héritent de Date.prototype, qui lui-même hérite de Object.prototype.

Par définition, le prototype Object n'a pas de prototype (il est au sommet de la hiérarchie).

Il est également possible de créer des objets sans prototype (prototype-less), avec var objSansPrototype = Object.create(null).

Prototype par défaut

Par défaut,

  • tous les objets créés (avec la notation {...}) ont pour prototype Object.prototype.
  • toutes les fonctions ont pour prototype Function.prototype.

Ces deux prototypes existent "nativement" en Javascript, ce sont eux qui proposent des méthodes communes à tous les objets/fonctions, comme par exemple toString() pour les objets, ou call, bind, apply pour les fonctions.

Surcharge et polymorphisme

En orienté objet, le polymorphisme est la capacité pour une fonction de produire une résultat différent en fonction du contexte dans laquelle elle est invoquée.

Pour réaliser ceci, il suffit de déclarer dans l'objet, la fonction que l'on souhaite surcharger.

Surcharge et polymorphisme

Pour éviter de dupliquer dans un objet, le code métier qui serait dans une fonction de son prototype, on peut directement appeler celle-ci.

Pour ce faire, on utiliser protoype.fonction.call(this,...) pour bien référencer la fonction du prototype, mais invoquée sur l'objet courant.

Propriétés propres vs. héritées

Pour savoir si une propriété est propre à un objet ou si elle a été héritée, il est possible d'utiliser la méthode hasOwnProperty(prop) héritée du prototype de Object.

La POO en Javascript Classes et instanciation

Il y a une bonne et une mauvaise nouvelle :

  • Javascript n'est pas un langage de classe, un langage dans lequel les classes sont définies proprement, et la hiérachie d'héritage est clairement établie.
  • Il existe néanmoins une syntaxe proche de celle des langages de programmation objet qui permet de retrouver les mots-clés classiques.
  • Toutefois, quand on connaît le mécanisme sous-jacent, on sait et on peut l'utiliser à bon escient pour construire des objets et une hiérarchie d'objet qui fonctionne.

Nous allons désormais voir comment mettre en place la structuration présentée précédemment, dans un contexte plus générique, proche de celui de classes et d'héritage qui est plus classique en POO.

Une excellente ressource sur le sujet : http://www.objectplayground.com/
(forte source d'inspiration de cette partie)

Organiser ses prototypes

Un premier principe est d'utiliser un prototype pour contenir les fonctions de nos objets, et de construire les objets à partir de ce prototype.

Organiser ses prototypes
Organiser ses prototypes

Toujours sur le même principe, on peut créer un second prototype, à partir du premier, pour les objets plus spécialisés. Le nouveau prototype contient les nouvelles functions et les fonctions surchargées.

On retrouve une structuration classique en classes, instances, et sous-classes. Mais niveau technique, c'est pas top : logique métier dupliquée, violation de l'encapsulation.

Organiser ses prototypes

Pour pallier au problème d'accéder aux propriétés d'un objet et de devoir dupliquer ce type d'action, on va utiliser un constructeur pour initialiser les propriétés de nos objets.

Organiser ses prototypes
Construire des objets

L'approche basée sur les prototypes proposée ici fonctionne, dans le sens où elle va bien construire une chaîne de prototype correctement structurée.

Toutefois, on constatera que l'opération de créer un objet et de l'initialiser avec un constructeur est une action classique, qui est habituellement réalisée par le mot-clé new dans les langages de programmation.

Le modèle classique est une autre manière, plus conventionnelle, de construire des objets. Il s'agit de la représentation que vous rencontrerez le plus souvent.

Il y a deux manières de déclarer ses objets :

  • Utiliser des fonctions : une fonction définit une classe (notation/approche historique)
  • Utiliser des déclarations de classe (notation moderne - ES2015)
Les objets - Construire des objets avec des fonctions

Lorsque l'on déclare une fonction en Javascript, un objet Function est créé en mémoire, celui-ci possède en réalité 3 propriétés : un nom, un taille et...un prototype.

La particularité est que cette propriété prototype de notre fonction pointe sur un objet (créé automatiquement en mémoire) présentant une propriété constructor qui renvoie sur... la fonction elle-même.

Les objets - Construire des objets avec des fonctions

Cela ressemble à quelque chose que l'on connaît...

Oui, c'est le modèle de prototype vu précedemment.

Les objets - Construire des objets avec des fonctions

Chaque function possède son propre prototype associé :

  • La plupart du temps, quand on déclare des fonctions qui n'ont pas de but spécifique, celui-ci ne servira rien.
  • Par contre, si on utilise ces fonctions comme constructeurs d'objet, le prototype de la méthode sera utilisé.

C'est l'appel de la fonction avec le mot-clé new qui aura cet effet.

  • un nouvel objet est créé
  • ses propriétés sont initialisées par le code de la fonction (via this.XXX = ...)
  • le prototype de l'objet créé sera...le prototype contenu dans la propriété éponyme de la fonction utilisée
Les objets - Construire des objets avec des fonctions
Les objets - Construire des objets avec des fonctions Héritage avec prototype

Pour réaliser un héritage avec un prototype, il y a 3 actions à réaliser :

  • déclarer un constructeur sous la forme d'une fonction pour permettre de créer l'objet ; ceci va avoir pour effet corrolaire de créer un prototype pour cette fonction.
    function MaSousClasse(...) { ... }
  • ce prototype n'étant pas désiré (on souhaiterait qu'il pointe vers la "classe-mère"), on va créer un objet pour le prototype de cette fonction.
    MaSousClasse.prototype = Object.create(MaFonction.prototype);
    L'ancien prototype sera ainsi détaché de la fonction et détruit par le garbage collector.
  • spécifier que le constructeur de ce nouveau prototype est la fonction constructeur de la sous-classe
Les objets - Héritage
Les objets - Héritage
Les objets - Définir des méthodes statiques

On peut aussi créer des méthodes statiques en les ajoutant directement à la fonction constructeur et non pas à son prototype.

Les objets - Définir des attributs privés

Dans cette représentation, il est possible de déclarer des attributs comme privés en "trichant" sur leur déclaration : on déclare une variable locale à la fonction (sans this) et on laisse la notion de closure faire le reste, pour y accéder au sein des différentes méthodes.

Prototypes vs. Classes
Langages de classe (Java)Langage de prototype (Javascript)
Les classes et les instances sont deux entités distinctes. Tous les objets sont des instances.
Une classe est définie avec une définition de classe. On instancie une classe avec des méthodes appelées constructeurs. On définit et on crée un ensemble d’objets avec des fonctions qui sont des constructeurs.
On crée un seul objet grâce à l’opérateur new.
On construit une hiérarchie d’objets en utilisant les définitions des classes pour définir des classes-filles à partir de classes existantes. On construit une hiérarchie d’objets en assignant un prototype à un objet dans le constructeur de cet objet.
Les objets héritent des propriétés appartenant à la chaîne des classes de la hiérarchie. Les objets héritent des propriétés appartenant à la chaîne des prototypes de la hiérarchie.
La définition de la classe définit exactement toutes les propriétés de toutes les instances d’une classe. Il est impossible d’ajouter des propriétés dynamiquement pendant l’exécution. Le constructeur ou le prototype définit un ensemble de propriétés initiales. Il est possible d’ajouter ou de retirer des propriétés dynamiquement, pour certains objets en particuliers ou bien pour l’ensemble des objets.
ES2015 : arrivée des déclarations de classes

ECMAScript 2015 a rajouté au standard des mots-clés bien connus : class, extends, super comme sucre syntaxique pour les constructions définies précédemment.

Les méthodes sont déclarées sans utiliser mot-clé function.

Les classes présentent une méthode constructor qui permet d'initialiser les attributs de l'objet ainsi créé.

NB. les déclarations de classes ne sont pas hissées (hoisted).

ES2015 : arrivée des déclarations de classes

Jusqu'à présent, les objets créés encapsulent des attributs ayant une visibilité publique : ils sont nécessairement accessibles depuis l'extérieur puisque déclarés avec this..

Il est pourtant possible de rendre des attributs et des méthodes privées au sein d'un objet, en les déclarant et en les nommant avec le préfixe # (nouveauté ES2022).

L'opérateur instanceof

Bien connu des adeptes de la programmation objet, cet opérateur permet de tester le type d'un objet.

Cet opérateur renvoie true à l'expression o instanceof C si C appartient à la chaîne de prototypes du constructeur de o.

JSON JSON

JSON signifie JavaScript Object Notation. Il s'agit d'un format textuel pour stocker (enregistrer des objets structurés) et échanger des données (en les sérialisant via des chaînes de caractères).

La syntaxe de JSON s'inspire de la syntaxe des objets Javascript :

  • les données sont présentées sous la forme clé : valeur où la clé est obligatoirement délimitée par des guillemets doubles (ex. "prenom" : "fred") ;
  • les données sont séparées par des virgules ,
  • les objets sont délimités par des accolades {...}
  • les tableaux sont délimités par des crochets [...]

A noter que les chaînes sont obligatoirement délimitées par des guillemets doubles. Les guillemets simples ne peuvent pas être utilisés comme délimiteurs.

JSON

Tous les types de données ne sont pas autorisés pour être enregistrés au format JSON. En effet, celui-ci autorise :

  • les nombres avec ou sans partie décimale,
  • les booléens
  • les chaînes (obligatoirement délimitées par des guillemets doubles)
  • les tableaux
  • le objets (sous la forme d'un objet JSON)
  • la valeur null

De ce fait, les dates, les fonctions et undefined ne sont pas autorisés.

Bien que nommé à partir de Javascript, cette notation est un format purement textuel que l'on retrouve dans différents langages : PHP, Python, Ruby, Java, etc. Il s'agit d'une bonne alternative à XML pour échanger des données structurées.

JSON

En tant que telle la notation JSON n'a pas vraiment d'avantages par rapport à la notation objet classique de Javascript. L'obligation d'entourer les noms de propriétés de guillemets aurait même tendance à rendre JSON moins facile d'usage.

L'utilisation principale d'un objet JSON est d'offrir un mécanisme de stockage et d'échange plus simple et flexible qu'XML.

Dans tous les langages, il existe des primitives permettant de sérialiser un objet (d'en faire une chaine de caractères) et de le désérialiser (le créer à partir d'une chaîne). En Javascript, ces méthodes sont les suivantes :

  • JSON.stringify(obj) renvoie la chaîne de caractères sérialisant l'objet obj.
  • JSON.parse(chaine) renvoie l'objet extrait de la chaîne de caractères décrivant l'objet.
JSON

L'objet décrit au format JSON dans la zone de texte sera converti en "vrai" objet Javascript.

Trucs en vrac à connaître
Plan du chapitre Trucs à connaître

Javascript gère automatiquement la mémoire. Il possède ainsi un garbage collector (tout comme Java) qui permet régulièrement de nettoyer les objets qui ne sont plus rattachés à aucune donnée.

En conséquence, ce n'est pas à vous de gérer la suppression des objets. Attention toutefois à ne pas surcharger la mémoire de petits objets, ou de petites variables, pour économiser les ressources.

Javascript possède un mot-clé debugger représentant une instruction qui, lorsqu'elle est exécutée dans un environnement à peine évolué (a minima un navigateur web avec des outils développeurs -- la plupart en proposent), permet de stopper l'exécution du code et d'ouvrir le debugger pour offrir les possibilités classiques de ce genre d'outil : inspection de la mémoire, exécution pas-à-pas, etc.

Destructuration

Pour instancier plusieurs variables en même temps, il est possible de les assimiler à une donnée structurée qui sera "déstructurée" pour faire correspondre aux valeurs.

Mode strict

Le mode strict en Javascript a été introduit pour signaler comme erreur des constructions du langages qui sont souvent inappropriées (utilisation de variables non déclarées, etc.)

Pour utiliser ce mode, on déclare "use strict"; en début de script, ou en début de fonction pour une portée locale (mais toujours au début sinon il est ignoré).

Interdit en mode strictPourquoi
x = "ouaf";Interdit si x n'est pas déclaré.
x = "ouaf";
delete x;
Interdit de supprimer une variable x
function f(p,p)Paramètre p dupliqué
var eval = ...Mot clé réservé
var arguments = ...Mot clé réservé, idem pour public, etc.
with (Math) { var x = cos(2); }Construction with interdite
eval("var x = 42;")eval ne peut pas créer de variable
Linter

JSLint, ESLint ou JSHint sont des "linter", c'est-à-dire des logiciels capables de réaliser (un peu) l'analyse statique du code Javascript en vue d'y détecter des erreurs, des mauvaises constructions, etc.

Ils sont utilisables en ligne, soit comme extension de vos éditeurs de code préférés (pour peu que vous utilisiez autre chose que gedit ou textedit...).

Bien qu'utiles, ils ne remplacent pas une bonne phase de test, mais ils permettent au moins de détecter pas mal de petites erreurs. Ils sont généralement paramétrables pour fixer la granularité des erreurs détectées.

Certains d'entre eux peuvent s'utiliser en ligne :
JSLint : http://www.jslint.com/ ou JSHint : http://jshint.com/

Typescript

Typescript est tout simplement une version typée de Javascript, dans laquelle chaque variable, paramètre, fonction, etc. déclare un type parmi les types primitifs (number, etc.) ou dérivés (Array<number>, etc.).

Typescript

Un compilateur est en charge de réaliser l'analyse statique des types et ainsi de s'assurer de l'utilisation correcte des données. Si aucune erreur n'est levée, un fichier JS "pur" est ainsi créé et est prêt à être exécuté (côté client ou serveur).

Certains éditeurs de code (comme VSCode) sont capables d'analyser les sources et de lever ainsi certaines erreurs avant même la compilation.

Plus d'infos sur https://www.typescriptlang.org/

Ressources
  • The Principles of Objet-Oriented Javascript - N. Zakas
  • Javascript for Web Developers - Nicholas Zakas
  • Eloquent Javascript - Marijn Haverbeke
  • Secrets of the Javascript Ninja - John Resig et Bear Bibeault
  • Référence complète : https://developer.mozilla.org/fr/docs/Web/JavaScript
  • Tutoriels et référence : http://w3schools.com
  • Exploring JS : http://exploringjs.com/
  • Javascript is sexy: http://javascriptissexy.com/
  • Et tellement d'autres...
Ressources
  • Comment fonctionnent les interpréteurs Javascript ?
    https://www.youtube.com/watch?v=p-iiEDtpy6I
  • Functional programming with Javascript :
    https://www.youtube.com/watch?v=e-5obm1G_FY
  • Toutes les vidéos de Douglas Crockford, notamment celles-ci :
    https://www.youtube.com/watch?v=v2ifWcnQs6M, https://www.youtube.com/watch?v=DwYPG6vreJg et https://www.youtube.com/watch?v=NPB34lDZj3E

A vous de jouer :
https://h5bp.github.io/Front-end-Developer-Interview-Questions/translations/french/#js-questions ou https://morioh.com/p/620129429e84

Prochaine étape... Javascript dans le navigateur !