Dans la majeure partie des langages, on s'attend à ce qu'une variable déclarée dans un bloc ne soit disponible que dans celui-ci
Ce n'est pas le cas d'une variable déclarée avec le mot clé var
for(var i = 0; i < 5; i++) {
console.log(i);
}
console.log(i); // affiche 5
if(true) {
var name = "Smith";
}
console.log(name); // affiche "Smith"
Depuis la version 6 du standard ECMAScript (ou ES2015), on peut utiliser un nouveau mot clé pour déclarer une variable de bloc
Une variable déclarée avec le mot clé let n'est disponible que dans le bloc dans lequel elle a été déclarée !
for(let i = 0; i < 5; i++) {
console.log(i);
}
console.log(i); // undefined
if(true) {
let name = "Smith";
}
console.log(name); // undefined
Le standard ES2015 apporte aussi une généralisation du mot clé const permettant de déclarer une constante !
Les constantes ont aussi une portée limitée au bloc dans lequel elles sont déclarées !
On l'utilise de plus en plus souvent
const x = 1;
x = 2; // Erreur !!
// Impossible de réassigner !
// Attention :
const personne = {
prenom: 'Lior',
nom: 'Chamla'
};
personne.prenom = 'Elise'; // Possible
personne = 12; // Erreur !
// Impossible de réassigner !
C'est une nouvelle façon d'écrire des strings. Grâce aux backticks (les accents grave), on déclare des chaines dans lesquelles on peut :
On interpole des valeurs grâce à la syntaxe : ${...}
const prenom = "Lior";
const nom = "Chamla";
const html = `
<p>
Dans cette syntaxe, on peut aller
à la ligne et formater
comme on le souhaite !
</p>
<p>
Au fait, je suis ${prenom} ${nom} !
</p>
`;
Javascript vous permet de regrouper des préoccupations similaires au sein d'objets (une personne, un requête HTTP, une facture ...)
Un objet est un ensemble de données (propriétés) et de comportements (méthodes)
const personne = {
// propriétés
prenom: 'Lior',
nom: 'Chamla',
// méthodes
marcher() {}
parler() {}
}
personne.marcher();
console.log(personne.prenom);
// affiche 'Lior'
personne.prenom = 'Elise';
// modifie la propriété
On peut accéder aux données (propriétés) d'un objet par deux syntaxes différentes : objet.propriete ou objet["propriete"] !
const personne = {
// propriétés
prenom: 'Lior',
nom: 'Chamla',
// méthodes
marcher() {}
parler() {}
}
console.log(personne.prenom);
// affiche 'Lior'
console.log(personne['prenom']);
// affiche 'Lior'
const property = 'prenom';
console.log(personne[property]);
// affiche 'Lior'
La variable this représente toujours le contexte d'exécution !
Au sein d'un objet, elle représente l'objet lui-même (bien pratique 😁)
En dehors, elle représente le contexte
const personne = {
prenom: 'Lior',
nom: 'Chamla',
parler() {
console.log("Je suis " + this.prenom);
}
}
personne.parler();
// affiche : "Je suis Lior"
console.log(this);
// représente l'objet global "window"
// this n'est pas le même en fonction
// du contexte dans lequel on fait appel
// à lui !
Il est parfois utile, lorsqu'on définit une référence à une fonction de lui préciser ce que this doit représenter dans la fonction !
Dans le cas contraire, il représentera le contexte d'exécution
const personne = {
prenom: 'Lior',
nom: 'Chamla',
parler() {
console.log("Je suis " + this.prenom);
}
}
personne.parler();
// affiche : "Je suis Lior"
button.addEventListener('click', personne.parler);
// affichera "Je suis undefined"
// car this représentera le bouton
button.addEventListener(
'click',
personne.parler.bind(personne)
);
// ici on précise qu'au sein de "parler"
// this représentera l'objet "personne"
Depuis la version 6 d'ECMAScript (ES2015), on peut utiliser une notation très élégante pour déclarer une fonction : la fonction fléchée !
Elle apporte bien plus que de l'élégance, un véritable avantage à voir ensuite ...
// Syntaxe classique
const additionner = function(x, y) {
return x + y;
}
console.log(additionner(10, 10));
// affiche 20
// Fonction fléchée (arrow function)
const additionner = (x, y) => x + y;
console.log(additionner(10, 10));
// affiche 20
Un avantage qu'apporte cette nouvelle syntaxe est son expressivité en plus de son élégance.
const nombres = [10, 8, 9, 22, 30, 2, 50];
// Filtre avec syntaxe classique
// Assez "rugueux" à lire
const inferieursA10 = nombres.filter(function(nombre){
return nombre < 10;
});
// Filtre avec syntaxe fléchée
// Beaucoup plus expressif et clair
const inferieursA10 = nombres.filter(nombre => nombre < 10);
// Je ne prend que les nombres qui sont inférieurs à 10
La fonction fléchée conserve le contexte d'exécution !
Elle permet de continuer à utiliser this au sein de fonctions de rappel (callback) !
La fonction fléchée ne recréé par de nouveau lien pour this !
const personne = {
prenom: 'Lior',
presenter() {
setTimeout(function(){
console.log('Je suis ' + this.prenom);
}, 1000);
},
presenterArrow() {
setTimeout(() => {
console.log('Je suis ' + this.prenom);
}, 1000);
}
};
personne.presenter();
// affiche "Je suis undefined" après 1 seconde
// this ici ne fait pas référence à l'objet
// personne mais à la fonction dans laquelle
// il est appelé (contexte d'exécution global)
personne.presenterArrow();
// affiche "Je suis Lior" après 1 seconde
// this continue de représenter l'objet !
Ce sont des fonctions qui nécessitent une autre fonction (callback) pour s'exécuter !
Particulièrement utiles sur les arrays (tableaux) !
const nombres = [10, 20, 30, 40, 50];
// Filtrage :
const inferieursA30 = nombres.filter(function(nombre){
return nombre < 30;
});
// Transformation :
const multipliesPar2 = nombres.map(function(nombre){
return nombre * 2;
});
// Réduction :
const somme = nombres.reduce(function(total, nombre){
return total + nombre;
}, 0);
// Recherche d'index :
const index = nombres.findIndex(function(nombre) {
return nombre === 20;
});
D'autres fonctions de haut niveau pour les tableaux :
const nombres = [10, 20, 30, 40, 50];
// Contient
const hasPair = nombres.some(function(nombre){
return nombre % 2 === 0;
}); // boolean
// Tous
const hasOnlyPairs = nombre.every(function(nombre){
return nombre % 2 === 0;
}); // boolean
// Tri
nombres.sort(function(a, b) {
return a - b;
});
const personne = {
prenom: 'Lior',
nom: 'Chamla',
ville: 'Marseille'
}
// A l'ancienne :
const prenom = personne.prenom;
const nom = personne.nom;
// Destructuring :
const { prenom, nom } = personne;
// Avec alias :
const { prenom: firstName, nom: lastName } = personne;
console.log(firstName); // affiche 'Lior'
const personne = ['Lior', 'Chamla', 'Marseille'];
// A l'ancienne :
const prenom = personne[0];
const nom = personne[1];
// Destructuring :
const [ prenom, nom ] = personne;
console.log(prenom); // affiche 'Lior'
Il permet d'éclater une structure telle qu'un tableau ou un objet !
const nombres = [10, 12, 2, 40];
// ...nombres
// 10, 12, 2, 40
// Un peu comme si on enlevait les crochets donc
// Math.max(...nombres) équivaut à
// Math.max(...[10, 12, 2, 40]) qui équivaut à
// Math.max(10, 12, 2, 40)
const lettres = ['a', 'b', 'c'];
const fusion = [...nombres, ...lettres];
// équivaut à [...[10, 12, 2, 40], ...['a','b','c']
// équivaut à [10, 12, 2, 40, 'a', 'b', 'c']
const personne = { prenom: 'Lior', nom: 'Chamla' };
// ...personne donne
// prenom: 'Lior', nom: 'Chamla'
const personne2 = { ville: 'Marseille', ...personne };
// équivaut à { ville: 'Marseille', ...{prenom: 'Lior', nom: 'Chamla'} }
// équivaut à { ville: 'Marseille', prenom: 'Lior', nom: 'Chamla' }
ES6 apporte avec lui la notion de Classes (POO). Bien que ce ne soit pas un véritable système de classes (JS est un langage qui repose sur les prototypes), c'est une syntaxe qui rassure les programmeurs d'autres langages !
class Personne {
type = "humain";
constructor(nom, prenom) {
this.nom = nom;
this.prenom = prenom;
}
presenter() {
console.log(`
Je suis ${this.prenom} ${this.nom}
`);
}
}
const prof = new Personne('Chamla', 'Lior');
prof.presenter();
// affiche "Je suis Lior Chamla"
Avec la notion de classes, ES6 apporte aussi la notion d'héritage !
Une classe qui hérite d'une autre classe bénéficie des comportements de celle-ci !
On utilise le mot clé extends pour indiquer qu'une classe hérite des comportements d'une autre !
class Banquier extends Personne {
encaisser() {
console.log('Veuillez payer !!');
}
}
const banquier = new Banquier('Chamla', 'Lior');
// Le constructor appelé est celui
// de la classe Personne !
banquier.encaisser();
// affiche "Veuillez payer !!"
banquier.presenter();
// affiche "Je suis Lior Chamla"
// C'est la méthode qui se trouve
// dans la classe Personne !
ES6 apporte la notion de modules !
Elle permet d'améliorer la modularité du code en séparant le code en différents modules (fichiers)
Un module possède des données et comportements privés qu'il peut rendre public si il le souhaite
// fichier personne.js
class Personne {
constructor(nom, prenom){
this.nom = nom;
this.prenom = prenom;
}
presenter() {
console.log('Kikoo, je suis ' + this.prenom);
}
}
// On rend la classe Personne disponible
// depuis l'exterieur
export Personne;
// fichier app.js
// On importe la classe Personne du
// fichier personne.js
import { Personne } from './personne'
const prof = new Personne('Chamla', 'Lior');
Une donnée ou une fonction qui n'est pas exportée par un module ne peut pas être importée ailleurs.
On peut se servir de cela pour protéger certaines informations et comportements, un peu comme l'encapsulation dans le paradigme objet
// fichier counter.js
const counter = 0;
export function increment() {
counter++;
}
export function decrement() {
counter--;
}
export function get() {
return counter;
}
// fichier app.js
import { increment, decrement, get } from './counter';
increment(); // pas de soucis
get(); // pas de soucis, retourne 1
import { counter } from './counter';
// ERREUR : counter n'est pas un membre exporté
// dans le module ./counter !
Un module peut exporter un de ses membres par défaut !
// fichier counter.js
const counter = 0;
export function increment() {
counter++;
}
export function decrement() {
counter--;
}
export default function get() {
return counter;
}
// fichier app.js
import get, { increment, decrement } from './counter';
increment(); // pas de soucis
get(); // pas de soucis, retourne 1
import { counter } from './counter';
// ERREUR : counter n'est pas un membre exporté
// dans le module ./counter !