Généralités sur JavaScript
Bref historique
Années 1990 - Dynamic HTML – effets sur les pages web :
-
Langage écrit en 1995 par Brendan EICH chez Netscape, pour associer des scripts à des éléments HTML.
-
Permet d’avoir des programmes en plus des pages Web dans le navigateur.
Ces programmes permettent d’agir directement avec la page Web, sans rechargement. -
Standard 96-97 (ECMAScript)
actuellement version 14 (juin 2023),
nouvelle version tous les ans
Bref historique
Années 2000 – Librairies évoluées :
- Jquery, MooTools, AngularJS, … : proposent un ensemble de fonctions, ou même un cadre de travail complet pour JavaScript.
- AJAX (utilisation « asynchrone » de JavaScript pour gérer des appels au serveur de données). Voir TD5 et suivants.
Années 2010 – Ère moderne :
- Évolution de JavaScript : utilisation du langage côté serveur (retour aux origines).
- Tendance actuelle : un seul langage dans la pile web, par exemple remplacer PHP par JavaScript.
- Node.js pour des serveurs web écrits en JavaScript.
Node.js ~ PHP terminal CLI
Console navigateur ~ PHP lié au serveur Web Apache
Environnement de travail
Environnement d’exécution
-
la console des DevTools du navigateur :
Endroit idéal pour tester le code, en interaction directe avec la page web. Outil indispensable. Les exemples du cours sont testés dans la console. -
Node.js
:
Permet, entre autres, d’exécuter du JS dans un terminal.
⚠️ Non lié à une page Web
Environnement de développement
Nous vous conseillons WebStorm
ou VSCode
.
Caractéristiques générales
-
langage qui dynamise les pages web côté client
- Scripts interprétés dans le navigateur.
Page web dynamique côté client (voir TD1), souvent par une gestion des événements (clics, …). - Différent des pages dynamiques côté serveur (PHP),
où on gère des informations envoyées (formulaires, cookie) ou stockées dans la base de données, la session.
- Scripts interprétés dans le navigateur.
-
langage interprété
JavaScript est interprété au niveau du navigateur, sans la moindre compilation.
L’exécution des scripts dépend de l’activation, côté client, de l’interpréteur JavaScript.
Variables en JavaScript
Les types principaux
-
JavaScript propose 8 types différents, nous en utiliserons essentiellement 4 :
Number
(les nombres flottantsdouble
)String
(chaînes de caractères)
Immuable (comme Java, Python, à l’inverse de C++, PHP)Boolean
(les booléens)Object
(tous les objets JavaScript)
Dont les tableaux, stockés par référence (comme Java, …)
-
Les 4 autres types (
BigInt
,Null
,Undefined
etSymbol
) sont pour nous moins communs. -
Le typage JavaScript est :
- Faible : Pas de type indiqué à la déclaration.
- Dynamique : Le type d’une variable peut changer.
Déclaration des variables
- Les variables se déclarent par les mots-clés
var
,let
ouconst
. - La tendance actuelle est l’utilisation du mot-clé
let
. Nous privilégierons ce mot-clé.
let bianca = "Bianca Castafiore";
- Le mot-clé
const
fonctionne commelet
, à ceci près que la valeur ne peut pas être réaffectée après initialisation.
const pi = 3.141592653589793;
pi = 3;
// → TypeError: Assignment to constant variable.
Montrer la console de développement
Portée des variables
Variables locales
Une variable déclarée avec let
a pour portée le bloc contenant (fonction, boucle for
, …).
for(let i = 0; i < 2; i++) {
console.log(i)
}
// → 0
// → 1
console.log(i)
// → Uncaught ReferenceError: i is not defined
Remarque :
- JS permet des fois d’omettre le
;
à la fin de l’instruction. - Bonne pratique : Toujours mettre
;
à la fin.
Montrer node.js
Portée des variables
Variables locales
Une variable déclarée avec let
a pour portée le bloc contenant
(fonction, boucle for
, …).
let i;
for(i = 0; i < 2; i++) {
console.log(i);
}
// → 0
// → 1
console.log(i)
// → 2
Portée des variables
Variables locales
Une variable déclarée avec let
a pour portée le bloc contenant
(fonction, boucle for
, …).
function f() {
let j = 2;
console.log(j);
}
f();
// → 2
console.log(j);
// → Uncaught ReferenceError: j is not defined
Portée des variables
Variables globales
Une variable déclarée dans la partie principale du script a donc pour portée tout le script : c’est une variable globale
let i_global = 0;
function f(nb) {
i_global = nb;
}
f(8);
console.log(i_global);
// → 8
Opérateurs logiques
Entre variables de type Boolean
-
==
/!=
: égalité en valeur ⚠️ Danger ! ⚠️
Relation est symétrique, mais pas transitive en JS (ni en PHP)console.log([] == false); // → true console.log(false == "0"); // → true console.log("0" == []); // → false
-
===
/!==
: égalité en valeur et en type
À privilégier.console.log([] === false); // → false car types différents console.log(false === "0"); // → false console.log("0" === []); // → false
JavaScript est très permissif !
Conversions automatiques de type
console.log (8 * null);
// → 0
En effet, *
est nécessairement la multiplication de deux nombres donc null
est converti en un nombre
Number(null);
// → 0
// ATTENTION : çà ne marche que dans les cas simples
Number("five");
// → NaN
Number("5");
// → 5
Number(undefined);
// → NaN
Quizz 1/2
Question : Que rend le code suivant ?
console.log ("5" - 1);
Réponse : 4
, car -
est nécessairement la soustraction de deux nombres donc "5"
est converti en un nombre
Quizz 2/2
Question : Que rend le code suivant ?
console.log ("5" + 1);
Réponse : "51"
!
Il y a ambiguïté entre addition de nombres et concaténation de chaînes de caractères. L’opérateur le plus prioritaire en JavaScript est la concaténation.
Évaluation paresseuse de ||
et &&
Si expr1
est vrai, alors
(expr1 || expr2)
est vrai- et
expr2
n’est pas évalué
Si expr1
est faux, alors
(expr1 && expr2)
est faux- et
expr2
n’est pas évalué
Intérêt :
if (p !== null && p.nom == 'Eich')
// Si p est null, alors p.nom n'est pas évalué
// (donc pas de TypeError)
(comme en PHP, Java, C++, … voir aussi le chainage optionnel ?.
)
Considérez l’expression (expr1 || expr2)
…
Méthodes du type Number
let x = 3.141592653589793;
typeof(x); // → 'number'
x.__proto__;
donne ceci dans la console des outils de développement
On sait alors que l’on peut appeler
x.toPrecision(4); // → '3.142'
x.toFixed(4); // → '3.1416'
Type String
Syntaxe entre guillemets simples 'coucou'
ou "coucou"
doubles :
- même comportement, sauf échappement du délimiteur :
\'
→'
si délimiteur simple\"
→"
si délimiteur double
- saut de ligne avec
\n
, …
Syntaxe entre accent grave `coucou`
. Permet le remplacement de variables avec ${...}
:
let nom = 'Juste Leblanc';
let p = `<p> Bonjour ${nom} </p>`;
console.log(p);
// <p> Bonjour Juste Leblanc </p>
Méthodes du type String
let password = "@P9GXpXuF%sy";
password.length; // → 12
password.toUpperCase(); // → @P9GXPXUF%SY
password.charAt(6); // → 'X'
password.indexOf("%"); // → 9
password.split("X"); // → [ '@P9G', 'p', 'uF%sy' ]
(voir le __proto__
en détail !)
Tableaux en JavaScript
Déclarations possibles d’un tableau
Les tableaux JavaScript peuvent être déclarés par un appel à un
constructeur de l’objet natif de JavaScript Array
:
let tab = new Array("bonjour","salut","hello");
Mais il est plus simple de les déclarer par :
let tab = ["bonjour","salut","hello"];
Parcours d’un tableau
Boucle for
classique :
let tab=["bonjour", "hello", "salut", "coucou"];
for (let i=0; i < tab.length; i++) {
console.log(`mot n°${i} : ${tab[i]}`);
}
// mot n°0 : bonjour
// mot n°1 : hello
// mot n°2 : salut
// mot n°3 : coucou
Question : Comment JS évalue tab[i]
alors que i
est un flottant ?
Réponse : Il tronque i
pour garder sa partie entière.
Parcours d’un tableau
Boucle for...of
:
let tab=["bonjour", "hello", "salut", "coucou"];
for (let mot of tab) {
console.log(mot);
}
// bonjour
// hello
// salut
// coucou
Méthodes des tableaux
// Insertion
let tab = [1];
tab.push(2); // En fin de tableau
tab.unshift(3); // En début de tableau
console.log(tab); // → [3, 1, 2]
// Supression
let fin = tab.pop(); // En fin de tableau
console.log(tab); // → [3, 1]
console.log(fin); // → 2
let debut = tab.shift() // En début de tableau
// Concaténation
let tabconcat = tab.concat([6,7]);
console.log(tabconcat); // → [ 3, 1, 6, 7 ]
Regarder le __proto__
pour d’autres méthodes intéressantes…
Fonctions
Syntaxe
Déclaration (comme en PHP)
function square(x) {
return x * x;
};
Les variables peuvent stocker des fonctions !
Le code ci-dessus est équivalent à
let square = function (x) {
return x * x;
};
Fonctions anonymes
On appelle fonction anonyme une déclaration de fonction sans nom.
Exemple :
setTimeout(
function () { console.log("Boum"); },
2000
);
Syntaxe raccourcie : Fonctions fléchées (arrow function en anglais)
setTimeout( () => {console.log(Boum);}, 2000);
Les 2 syntaxes suivantes sont équivalentes
const square1 = (x) => { return x * x; };
const square2 = x => x * x;
Fonctions
Les fonctions sont des objets de «première classe» : elles peuvent être manipulées et échangées comme tous les autres objets JavaScript.
Exemple : Mettons une valeur fonction dans une variable
function square(x) {
return x * x;
};
// Affectation de la variable
let varfonc = square;
// Exécution de la fonction avec l'opérateur ()
varfonc(2);
// → 4
Fonctions
Une fonction peut prendre en argument une fonction
function boum() {
console.log('Boum!');
}
// setTimeout execute la fonction boum après 2s
setTimeout(boum, 2000);
Une fonction peut aussi renvoyer une fonction.
Objets en JavaScript
Création de façon littérale
On peut définir un objet en donnant des paires clés-valeurs :
let p = {nom: "Haddock", prenom: "Archibald"};
p.nom; // → "Haddock
// p peut être complété
p.profession = "marin";
console.log(p);
// → { nom: 'Haddock', prenom: 'Archibald', profession: 'marin' }
Note : PHP permettait aussi d’ajouter des attributs :
$p = new stdClass();
$p->nom = "Haddock";
$p->prenom = "Archibald";
Création de façon littérale
On peut aussi créer l’objet avec des méthodes ou encore les ajouter après coup :
p.parler = function () {
console.log("mille sabords !");
}
p.parler(); // → "mille sabords !"
Création selon un modèle
Façon plus classique de coder :
class Personne {
constructor(nom, prenom, profession) {
this.nom = nom;
this.prenom = prenom;
this.profession = profession;
}
parler() {
console.log("mille sabords !");
}
static espece = "humain";
}
let capitaine = new Personne("Haddock", "Archibald", "marin");
capitaine.nom; // → "Haddock"
capitaine.parler(); // → "mille sabords !"
Personne.espece; // → "humain"
Langage basé sur les prototypes
- JavaScript n’est pas basé sur les classes, mais sur les prototypes
- Un prototype est comme une maquette de classe dynamique
On peut modifier le prototype après coup :Personne.prototype.aurevoir = function() { console.log("Bon vent !"); }
capitaine
hérite dynamiquement des méthodes du prototype dePersonne
…capitaine.aurevoir(); // → "Bon vent !"
Fonctionnement des prototypes
Chaque objet se voit rattaché un prototype, qui est un objet.
typeof capitaine.__proto__ // → object
Donc le prototype a aussi son prototype, ce qui crée une chaîne de prototypes.
À la fin, on arrive au prototype Object
dont
le prototype est null
.
capitaine.__proto__.__proto__.__proto__ // → null
Deux instances de la même classe ont le même prototype
let capitaine = new Personne("Haddock", "", "");
let tournesol = new Personne("Triffon", "", "");
capitaine.__proto__ === tournesol.__proto__;
// → true
Chaine prototypale ~ héritage (liste des classes mères)
ECMAScript 2015 Object.getPrototypeOf() équivalent à la propriété non-standard JavaScript __proto__
Fonctionnement des prototypes
Chaque prototype contient les méthodes de son type
Object.getOwnPropertyNames(capitaine)
// → ['nom', 'prenom', 'profession']
Object.getOwnPropertyNames(capitaine.__proto__)
// → ['constructor', 'parler']
Object.getOwnPropertyNames(capitaine.__proto__.__proto__)
// → ['constructor', 'hasOwnProperty', 'toString', ...]
Quand on tape capitaine.parler()
, l’objet va chercher la méthode dans son prototype, sinon dans le prototype du prototype…
Du coup, capitaine
hérite dynamiquement des méthodes du
prototype de Personne
.
capitaine.__proto__.aurevoir = function() {
console.log("Bon vent !");
}
capitaine.aurevoir(); // → "Bon vent !"
Parcours des attributs / méthodes
On peut parcourir l’objet par une boucle for...in
:
let gaston = {
nom:"Lagaffe",
prenom:"Gaston",
profession:"gaffeur"
};
for(attribut in gaston)
console.log(`Gaston possède l'attribut ${attribut}`);
// Gaston possède l'attribut nom
// Gaston possède l'attribut prenom
// Gaston possède l'attribut profession
Tableaux associatifs ?
Les objets JavaScript peuvent être aussi vus comme des « tableaux associatifs » en lisant autrement leurs attributs :
let p = {nom:"Dupont",prenom:"Pierre",age:35};
p.nom; // → "Dupont"
p.prenom; // → "Pierre"
p.age; // → 35
p["nom"]; // → "Dupont"
p["pre" + "nom"]; // → "Pierre"
p["age"]; // → 35
Avantage de la syntaxe obj[expr]
: expr
est évalué