18 min de lecture · 3 835 mots

JavaScript ES2024 : Syntaxe moderne

JavaScript ES2024 : Syntaxe moderne

Variables et constantes

Déclarations

// const - immuable (référence)
const nom = 'Jean';
const age = 30;
// nom = 'Marie'; // Erreur

const user = { nom: 'Jean' };
user.nom = 'Marie'; // OK - objet mutable
// user = {}; // Erreur - référence immuable

// let - variable locale au bloc
let compteur = 0;
compteur = 1; // OK

if (true) {
    let local = 'bloc';
}
// console.log(local); // Erreur - hors scope

// var - éviter (scope fonction, hoisting)
var ancien = 'à éviter';

// Bonnes pratiques:
// 1. Toujours const par défaut
// 2. let si réassignation nécessaire
// 3. Jamais var

Destructuration

// Arrays
const [a, b] = [1, 2];
const [first, ...rest] = [1, 2, 3, 4]; // first=1, rest=[2,3,4]
const [x, , z] = [1, 2, 3]; // x=1, z=3 (skip 2)
const [val = 0] = []; // Valeur par défaut

// Objects
const { nom, age } = { nom: 'Jean', age: 30 };
const { nom: username, age: userAge } = user; // Renommer
const { nom = 'Anonyme' } = {}; // Valeur par défaut
const { address: { city } } = user; // Nested

// Paramètres fonction
function saluer({ nom, age = 18 }) {
    return ${nom} (${age} ans);
}

// Rest dans objets
const { id, ...userData } = user;

// Swap variables
[a, b] = [b, a];

Types de données

Types primitifs

// String
const str = 'simple quote';
const str2 = "double quote";
const template = Template literal avec ${variable};
const multiline = `
    Multiligne
    automatique
`;

// Number
const int = 42;
const float = 3.14;
const exp = 2.5e3; // 2500
const hex = 0xFF; // 255
const octal = 0o777; // 511
const binary = 0b1010; // 10
const big = 1000000; // Séparateurs (1000000)

// BigInt (nombres arbitrairement grands)
const bigInt = 9007199254740991n;
const big2 = BigInt(9007199254740991);

// Boolean
const vrai = true;
const faux = false;

// Null et Undefined
const nul = null; // Absence intentionnelle
let indefini; // undefined - non initialisé

// Symbol (identifiant unique)
const sym = Symbol('description');
const sym2 = Symbol('description');
console.log(sym === sym2); // false

Vérification de types

// typeof
typeof 'string'; // 'string'
typeof 42; // 'number'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof null; // 'object' (bug historique)
typeof {}; // 'object'
typeof []; // 'object'
typeof function() {}; // 'function'

// instanceof
[] instanceof Array; // true
{} instanceof Object; // true

// Array
Array.isArray([]); // true
Array.isArray({}); // false

// Nombre
Number.isNaN(NaN); // true
Number.isFinite(42); // true
Number.isInteger(42); // true

// Null et undefined
value === null;
value === undefined;
value == null; // null OU undefined

// Truthy / Falsy
// Falsy: false, 0, '', null, undefined, NaN
// Truthy: tout le reste
if (value) { / truthy / }

// Nullish coalescing (ES2020)
const result = value ?? 'default'; // default si null ou undefined
const result2 = value || 'default'; // default si falsy

Opérateurs modernes

Opérateurs logiques

// Nullish coalescing (??)
const user = null;
const name = user ?? 'Anonyme'; // 'Anonyme'
const name2 = user || 'Anonyme'; // Même résultat ici

const count = 0;
const value1 = count ?? 10; // 0 (car count n'est pas nullish)
const value2 = count || 10; // 10 (car 0 est falsy)

// Optional chaining (?.)
const city = user?.address?.city; // undefined si user ou address null/undefined
const fn = obj.method?.(); // Appel conditionnel
const item = arr?.[0]; // Accès array conditionnel

// Logical assignment (ES2021)
x ??= 5; // x = x ?? 5
x ||= 5; // x = x || 5
x &&= 5; // x = x && 5

// Exemples pratiques
let config = null;
config ??= getDefaultConfig(); // Assigne seulement si nullish

user.profile ||= createProfile(); // Assigne si falsy
user.valid &&= checkUser(user); // Assigne si truthy

Opérateurs avancés

// Spread operator (...)
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
const copy = [...arr1]; // Copie shallow

const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
const merge = { ...obj1, ...obj2 };

// Spread dans fonctions
function sum(...numbers) {
    return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4); // 10

Math.max(...[1, 5, 3]); // 5

// Exponentiation ()
2  3; // 8
Math.pow(2, 3); // 8 (ancienne méthode)

// Exponential assignment
let x = 2;
x = 3; // x = x  3 = 8

Fonctions

Fonctions fléchées

// Syntaxe basique
const add = (a, b) => a + b;
const square = x => x  x; // 1 param: parenthèses optionnelles
const log = () => console.log('test'); // 0 param: parenthèses requises

// Bloc de code
const complex = (a, b) => {
    const result = a + b;
    return result  2;
};

// Retour objet (parenthèses requises)
const makeUser = (nom, age) => ({ nom, age });

// Pas de this propre (hérite du contexte)
const obj = {
    value: 42,
    regular: function() { return this.value; }, // this = obj
    arrow: () => this.value // this = contexte parent
};

// Cas d'usage appropriés
[1, 2, 3].map(x => x  2);
setTimeout(() => console.log('delayed'), 1000);
promise.then(data => console.log(data));

// À éviter: méthodes d'objet, constructeurs

Paramètres

// Paramètres par défaut
function greet(name = 'Guest', age = 18) {
    return Hello ${name}, ${age} years old;
}

// Paramètres rest
function sum(first, ...numbers) {
    return numbers.reduce((acc, n) => acc + n, first);
}
sum(1, 2, 3, 4); // 10

// Destructuration paramètres
function drawCircle({ x = 0, y = 0, radius = 10 } = {}) {
    console.log(Circle at (${x}, ${y}) with radius ${radius});
}
drawCircle({ x: 5, radius: 20 });

// Arguments variables avec spread
function multiply(multiplier, ...numbers) {
    return numbers.map(n => n  multiplier);
}

Fonctions asynchrones

// Async/Await
async function fetchData() {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
}

// Gestion erreurs
async function getData() {
    try {
        const data = await fetchData();
        return data;
    } catch (error) {
        console.error('Error:', error);
        throw error;
    }
}

// Async arrow function
const fetchUser = async (id) => {
    const response = await fetch(/api/users/${id});
    return response.json();
};

// Await multiples
async function getAll() {
    const [users, posts] = await Promise.all([
        fetchUsers(),
        fetchPosts()
    ]);
    return { users, posts };
}

// Top-level await (modules ES)
const data = await fetchData();

// Async IIFE
(async () => {
    const data = await fetchData();
    console.log(data);
})();

Structures de données

Arrays (tableaux)

// Création
const arr = [1, 2, 3];
const arr2 = new Array(3); // [empty × 3]
const arr3 = Array.of(1, 2, 3); // [1, 2, 3]
const arr4 = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']

// Méthodes modernes

// map - transformer chaque élément
[1, 2, 3].map(x => x  2); // [2, 4, 6]

// filter - filtrer éléments
[1, 2, 3, 4].filter(x => x > 2); // [3, 4]

// reduce - réduire à une valeur
[1, 2, 3, 4].reduce((acc, x) => acc + x, 0); // 10

// find - trouver premier élément
[1, 2, 3].find(x => x > 1); // 2

// findIndex - index du premier élément
[1, 2, 3].findIndex(x => x > 1); // 1

// findLast / findLastIndex (ES2023)
[1, 2, 3, 2, 1].findLast(x => x === 2); // 2 (dernier)
[1, 2, 3, 2, 1].findLastIndex(x => x === 2); // 3

// some - au moins un élément valide
[1, 2, 3].some(x => x > 2); // true

// every - tous les éléments valides
[1, 2, 3].every(x => x > 0); // true

// includes - contient valeur
[1, 2, 3].includes(2); // true

// flat - aplatir tableau
[1, [2, [3, 4]]].flat(); // [1, 2, [3, 4]]
[1, [2, [3, 4]]].flat(2); // [1, 2, 3, 4]
[1, [2, [3, 4]]].flat(Infinity); // [1, 2, 3, 4]

// flatMap - map puis flat
[[1, 2], [3, 4]].flatMap(arr => arr.map(x => x  2)); // [2, 4, 6, 8]

// forEach - itérer (pas de return)
[1, 2, 3].forEach((x, i) => console.log(i, x));

// at - accès par index (négatif OK) (ES2022)
[1, 2, 3].at(0); // 1
[1, 2, 3].at(-1); // 3 (dernier)

// Modification

// push/pop - fin
arr.push(4); // Ajoute à la fin, retourne longueur
arr.pop(); // Retire fin, retourne élément

// unshift/shift - début
arr.unshift(0); // Ajoute au début
arr.shift(); // Retire début

// splice - modifier n'importe où
arr.splice(1, 2); // Retire 2 éléments à partir index 1
arr.splice(1, 0, 'a', 'b'); // Insère à index 1
arr.splice(1, 1, 'new'); // Remplace à index 1

// slice - extraire portion (non-mutant)
arr.slice(1, 3); // Éléments index 1 et 2
arr.slice(-2); // 2 derniers

// toSpliced / toReversed / toSorted (ES2023) - versions non-mutantes
const sorted = arr.toSorted((a, b) => a - b);
const reversed = arr.toReversed();

// concat - concaténer
[1, 2].concat([3, 4]); // [1, 2, 3, 4]

// join - convertir en string
[1, 2, 3].join('-'); // '1-2-3'

// Tri
arr.sort(); // Tri alphabétique
arr.sort((a, b) => a - b); // Tri numérique croissant
arr.sort((a, b) => b - a); // Tri décroissant
arr.reverse(); // Inverse ordre

// Utilitaires
arr.length; // Taille
arr.fill(0); // Remplit avec valeur
arr.fill(0, 2, 4); // Remplit index 2-3 avec 0

Objects (objets)

// Création
const obj = { nom: 'Jean', age: 30 };
const obj2 = new Object();
const obj3 = Object.create(null); // Sans prototype

// Propriétés

// Computed property names
const key = 'age';
const obj4 = { [key]: 30 }; // { age: 30 }

// Shorthand property
const nom = 'Jean';
const age = 30;
const user = { nom, age }; // { nom: 'Jean', age: 30 }

// Shorthand methods
const obj5 = {
    greet() { return 'Hello'; }, // Au lieu de: greet: function() {}
    async fetch() { return await getData(); }
};

// Getters/Setters
const person = {
    firstName: 'Jean',
    lastName: 'Dupont',
    get fullName() {
        return ${this.firstName} ${this.lastName};
    },
    set fullName(name) {
        [this.firstName, this.lastName] = name.split(' ');
    }
};

// Accès
obj.nom; // Dot notation
obj['nom']; // Bracket notation
obj?.nom; // Optional chaining

// Modification
obj.nom = 'Marie';
obj['age'] = 31;
delete obj.age;

// Méthodes Object

// Keys, values, entries
Object.keys(obj); // ['nom', 'age']
Object.values(obj); // ['Jean', 30]
Object.entries(obj); // [['nom', 'Jean'], ['age', 30]]

// fromEntries (inverse de entries)
Object.fromEntries([['nom', 'Jean'], ['age', 30]]); // { nom: 'Jean', age: 30 }

// assign - copier propriétés
Object.assign({}, obj); // Copie shallow
Object.assign(target, source1, source2); // Merge

// freeze - empêcher modifications
Object.freeze(obj);

// seal - empêcher ajout/suppression
Object.seal(obj);

// hasOwn (ES2022) - meilleur que hasOwnProperty
Object.hasOwn(obj, 'nom'); // true

// getOwnPropertyDescriptor
Object.getOwnPropertyDescriptor(obj, 'nom');

// defineProperty
Object.defineProperty(obj, 'readonly', {
    value: 42,
    writable: false,
    enumerable: true,
    configurable: false
});

// Prototype
Object.getPrototypeOf(obj);
Object.setPrototypeOf(obj, proto);

// Grouping (ES2024)
const items = [
    { type: 'fruit', name: 'apple' },
    { type: 'fruit', name: 'banana' },
    { type: 'vegetable', name: 'carrot' }
];
Object.groupBy(items, item => item.type);
// { fruit: [{...}, {...}], vegetable: [{...}] }

Set (ensemble)

// Création
const set = new Set([1, 2, 3]);
const set2 = new Set(); // Vide

// Ajout/Suppression
set.add(4);
set.add(4); // Pas de doublons
set.delete(2);
set.clear(); // Vide tout

// Vérification
set.has(1); // true
set.size; // Taille

// Itération
for (const value of set) {
    console.log(value);
}

set.forEach(value => console.log(value));

// Conversion
[...set]; // Vers array
Array.from(set);

// Utilités
// Supprimer doublons d'array
const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]

// Opérations ensemblistes
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);

// Union (ES2025 proposal)
const union = new Set([...a, ...b]); // [1, 2, 3, 4]

// Intersection
const intersection = new Set([...a].filter(x => b.has(x))); // [2, 3]

// Différence
const difference = new Set([...a].filter(x => !b.has(x))); // [1]

Map (dictionnaire)

// Création
const map = new Map([
    ['key1', 'value1'],
    ['key2', 'value2']
]);

// Clés de tout type (vs objet: string/symbol uniquement)
map.set(1, 'number key');
map.set({ id: 1 }, 'object key');
map.set(true, 'boolean key');

// Get/Set/Delete
map.get('key1'); // 'value1'
map.set('key3', 'value3');
map.delete('key2');
map.clear(); // Vide tout

// Vérification
map.has('key1'); // true
map.size; // Taille

// Itération (ordre d'insertion garanti)
for (const [key, value] of map) {
    console.log(key, value);
}

map.forEach((value, key) => console.log(key, value));

// Clés, valeurs, entries
map.keys(); // Iterator des clés
map.values(); // Iterator des valeurs
map.entries(); // Iterator [key, value]

// Conversion
Object.fromEntries(map); // Vers objet
new Map(Object.entries(obj)); // Depuis objet

WeakSet et WeakMap

// WeakSet - références faibles, garbage collected
const weakSet = new WeakSet();
const obj = {};
weakSet.add(obj);
weakSet.has(obj); // true
weakSet.delete(obj);
// Pas de .size, pas itérable

// WeakMap - comme Map mais clés faibles
const weakMap = new WeakMap();
const key = {};
weakMap.set(key, 'value');
weakMap.get(key); // 'value'
weakMap.has(key); // true
weakMap.delete(key);
// Pas de .size, pas itérable

// Utilité: attacher metadata sans empêcher GC

Classes

Syntaxe basique

// Déclaration
class Person {
    // Constructeur
    constructor(nom, age) {
        this.nom = nom;
        this.age = age;
    }

    // Méthode
    greet() {
        return Bonjour, je suis ${this.nom};
    }

    // Getter
    get info() {
        return ${this.nom} (${this.age} ans);
    }

    // Setter
    set nom(value) {
        this.nom = value.trim();
    }

    get nom() {
        return this.nom;
    }

    // Méthode statique
    static create(nom, age) {
        return new Person(nom, age);
    }

    // Propriété statique
    static espece = 'Humain';
}

// Utilisation
const person = new Person('Jean', 30);
person.greet();
Person.create('Marie', 25);

Héritage

class Animal {
    constructor(nom) {
        this.nom = nom;
    }

    parler() {
        return ${this.nom} fait un bruit;
    }
}

class Chien extends Animal {
    constructor(nom, race) {
        super(nom); // Appel constructeur parent
        this.race = race;
    }

    // Override
    parler() {
        return ${this.nom} aboie;
    }

    // Appeler méthode parente
    parlerAnimal() {
        return super.parler();
    }
}

const chien = new Chien('Rex', 'Labrador');
chien.parler(); // 'Rex aboie'
chien instanceof Chien; // true
chien instanceof Animal; // true

Propriétés et méthodes privées

class BankAccount {
    // Propriété privée (ES2022)
    #balance = 0;

    // Méthode privée
    #calculateInterest() {
        return this.#balance  0.05;
    }

    constructor(initialBalance) {
        this.#balance = initialBalance;
    }

    deposit(amount) {
        this.#balance += amount;
    }

    getBalance() {
        return this.#balance;
    }
}

const account = new BankAccount(100);
account.deposit(50);
account.getBalance(); // 150
// account.#balance; // Erreur - privé

Static blocks et champs (ES2022)

class Configuration {
    static #apiKey;
    static #apiUrl;

    // Static initialization block
    static {
        this.#apiKey = process.env.APIKEY;
        this.#apiUrl = process.env.APIURL;
        console.log('Configuration loaded');
    }

    static getApiKey() {
        return this.#apiKey;
    }
}

Modules ES6

Export

// export.js

// Export nommé
export const PI = 3.14159;
export function add(a, b) {
    return a + b;
}
export class User {
    constructor(nom) {
        this.nom = nom;
    }
}

// Export groupé
const multiply = (a, b) => a  b;
const divide = (a, b) => a / b;
export { multiply, divide };

// Export renommé
export { multiply as mult };

// Export default (un seul par module)
export default class Calculator {
    add(a, b) { return a + b; }
}

// Ou
class Calculator {}
export default Calculator;

// Mix default + nommés
export default Calculator;
export { PI, add };

Import

// import.js

// Import default
import Calculator from './export.js';

// Import nommé
import { PI, add } from './export.js';

// Import renommé
import { multiply as mult } from './export.js';

// Import tout
import  as math from './export.js';
math.PI;
math.add(1, 2);

// Mix default + nommés
import Calculator, { PI, add } from './export.js';

// Import dynamique (async)
const module = await import('./module.js');
module.default;

// Import avec condition
if (condition) {
    const { feature } = await import('./feature.js');
}

// Re-export
export { add } from './math.js';
export  from './utils.js';
export { default as Calculator } from './calc.js';

Promises et async

Promises

// Création
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve('Succès');
        } else {
            reject('Erreur');
        }
    }, 1000);
});

// Utilisation
promise
    .then(result => console.log(result))
    .catch(error => console.error(error))
    .finally(() => console.log('Terminé'));

// Chaînage
fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));

// Promise.all - toutes doivent réussir
Promise.all([promise1, promise2, promise3])
    .then(([result1, result2, result3]) => {
        console.log('Tous terminés');
    });

// Promise.allSettled - attend toutes (ES2020)
Promise.allSettled([promise1, promise2, promise3])
    .then(results => {
        results.forEach(result => {
            if (result.status === 'fulfilled') {
                console.log('Succès:', result.value);
            } else {
                console.log('Erreur:', result.reason);
            }
        });
    });

// Promise.race - première terminée
Promise.race([promise1, promise2])
    .then(result => console.log('Premier:', result));

// Promise.any - première réussie (ES2021)
Promise.any([promise1, promise2, promise3])
    .then(result => console.log('Premier succès:', result))
    .catch(error => console.log('Toutes échouées'));

// Promise helpers
Promise.resolve(value); // Promise résolue
Promise.reject(error); // Promise rejetée

Async/Await avancé

// Gestion erreurs
async function fetchData(url) {
    try {
        const response = await fetch(url);
        if (!response.ok) {
            throw new Error(HTTP error! status: ${response.status});
        }
        return await response.json();
    } catch (error) {
        console.error('Fetch error:', error);
        throw error;
    }
}

// Parallèle avec Promise.all
async function getAllData() {
    const [users, posts, comments] = await Promise.all([
        fetchUsers(),
        fetchPosts(),
        fetchComments()
    ]);
    return { users, posts, comments };
}

// Séquentiel
async function processSequential() {
    const user = await fetchUser();
    const posts = await fetchUserPosts(user.id);
    const comments = await fetchPostComments(posts[0].id);
    return { user, posts, comments };
}

// Avec timeout
async function fetchWithTimeout(url, timeout = 5000) {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);

    try {
        const response = await fetch(url, { signal: controller.signal });
        return await response.json();
    } catch (error) {
        if (error.name === 'AbortError') {
            throw new Error('Request timeout');
        }
        throw error;
    } finally {
        clearTimeout(timeoutId);
    }
}

// Retry logic
async function fetchWithRetry(url, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            return await fetch(url).then(r => r.json());
        } catch (error) {
            if (i === retries - 1) throw error;
            await new Promise(resolve => setTimeout(resolve, 1000  (i + 1)));
        }
    }
}

DOM Manipulation

Sélection d’éléments

// Modern selectors
document.querySelector('#id'); // Premier élément
document.querySelector('.class');
document.querySelector('div.class');
document.querySelectorAll('.class'); // NodeList de tous

// Méthodes classiques
document.getElementById('id');
document.getElementsByClassName('class'); // HTMLCollection
document.getElementsByTagName('div');

// Traversal
element.closest('.parent'); // Ancêtre le plus proche
element.matches('.class'); // Teste si correspond au sélecteur
element.parentElement;
element.children; // HTMLCollection
element.firstElementChild;
element.lastElementChild;
element.nextElementSibling;
element.previousElementSibling;

Manipulation

// Contenu
element.textContent = 'Texte'; // Texte seul
element.innerHTML = 'HTML'; // HTML (attention XSS)
element.innerText = 'Texte visible'; // Texte visible (respecte CSS)

// Attributs
element.getAttribute('data-id');
element.setAttribute('data-id', '123');
element.removeAttribute('data-id');
element.hasAttribute('data-id');

// Data attributes
element.dataset.id = '123'; // data-id="123"
element.dataset.userId; // data-user-id

// Classes
element.classList.add('class1', 'class2');
element.classList.remove('class1');
element.classList.toggle('active');
element.classList.contains('active');
element.classList.replace('old', 'new');

// Style
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.display = 'none';
element.style.cssText = 'color: red; background: blue;';

// Computed style
getComputedStyle(element).color;

// Création
const div = document.createElement('div');
div.className = 'my-class';
div.id = 'my-id';
div.textContent = 'Contenu';

// Insertion
parent.appendChild(div);
parent.append(div, 'texte', otherElement); // Multiple
parent.prepend(div); // Au début
parent.insertBefore(div, referenceElement);
element.insertAdjacentElement('beforebegin', div); // Avant element
element.insertAdjacentElement('afterbegin', div); // Début de element
element.insertAdjacentElement('beforeend', div); // Fin de element
element.insertAdjacentElement('afterend', div); // Après element

// Suppression
element.remove();
parent.removeChild(element);

// Remplacement
parent.replaceChild(newElement, oldElement);
element.replaceWith(newElement);

// Clone
const clone = element.cloneNode(true); // true = deep clone

Événements

// addEventListener (moderne)
element.addEventListener('click', (event) => {
    console.log('Cliqué!');
    event.preventDefault(); // Empêche comportement par défaut
    event.stopPropagation(); // Arrête propagation
});

// Options
element.addEventListener('click', handler, {
    once: true,      // Se retire après 1 appel
    passive: true,   // Ne peut pas preventDefault
    capture: true    // Phase capture
});

// Retirer listener
element.removeEventListener('click', handler);

// Événements courants
element.addEventListener('click', handler);
element.addEventListener('dblclick', handler);
element.addEventListener('mouseenter', handler);
element.addEventListener('mouseleave', handler);
element.addEventListener('mousemove', handler);
element.addEventListener('keydown', handler);
element.addEventListener('keyup', handler);
element.addEventListener('input', handler); // Tout changement input
element.addEventListener('change', handler); // Changement validé
element.addEventListener('submit', handler);
element.addEventListener('focus', handler);
element.addEventListener('blur', handler);
element.addEventListener('scroll', handler);
element.addEventListener('resize', handler);

// Event object
function handleClick(event) {
    event.target; // Élément cliqué
    event.currentTarget; // Élément avec listener
    event.type; // Type d'événement
    event.key; // Touche clavier
    event.clientX, event.clientY; // Position souris
    event.preventDefault();
    event.stopPropagation();
    event.stopImmediatePropagation(); // Arrête tous handlers
}

// Délégation d'événements
document.querySelector('.list').addEventListener('click', (event) => {
    if (event.target.matches('.item')) {
        console.log('Item cliqué:', event.target);
    }
});

// Custom events
const customEvent = new CustomEvent('myEvent', {
    detail: { message: 'Hello' },
    bubbles: true,
    cancelable: true
});
element.dispatchEvent(customEvent);

element.addEventListener('myEvent', (event) => {
    console.log(event.detail.message);
});

Fetch API

// GET simple
fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));

// Avec async/await
async function getData() {
    try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) {
            throw new Error(HTTP error! status: ${response.status});
        }
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Fetch error:', error);
    }
}

// POST avec JSON
fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        nom: 'Jean',
        email: 'jean@example.com'
    })
})
.then(response => response.json())
.then(data => console.log(data));

// Autres méthodes
fetch(url, { method: 'PUT', body: JSON.stringify(data) });
fetch(url, { method: 'PATCH', body: JSON.stringify(data) });
fetch(url, { method: 'DELETE' });

// Headers
fetch(url, {
    headers: {
        'Authorization': 'Bearer token',
        'Content-Type': 'application/json',
        'X-Custom-Header': 'value'
    }
});

// FormData
const formData = new FormData();
formData.append('nom', 'Jean');
formData.append('file', fileInput.files[0]);

fetch(url, {
    method: 'POST',
    body: formData
});

// Abort (cancel request)
const controller = new AbortController();
fetch(url, { signal: controller.signal })
    .then(response => response.json())
    .catch(error => {
        if (error.name === 'AbortError') {
            console.log('Request cancelled');
        }
    });

// Cancel après timeout
setTimeout(() => controller.abort(), 5000);

// Response methods
response.json(); // Parse JSON
response.text(); // Texte brut
response.blob(); // Blob (fichiers)
response.arrayBuffer(); // ArrayBuffer
response.formData(); // FormData

// Response properties
response.ok; // true si 200-299
response.status; // Code HTTP
response.statusText; // Message HTTP
response.headers.get('content-type');

Utilitaires et patterns

String methods

const str = 'Hello World';

// Recherche
str.includes('World'); // true
str.startsWith('Hello'); // true
str.endsWith('World'); // true
str.indexOf('World'); // 6
str.lastIndexOf('o'); // 7
str.search(/world/i); // 6 (regex)

// Extraction
str.slice(0, 5); // 'Hello'
str.substring(0, 5); // 'Hello'
str.substr(0, 5); // 'Hello' (déprécié)
str.charAt(0); // 'H'
str.at(-1); // 'd' (ES2022)

// Transformation
str.toLowerCase();
str.toUpperCase();
str.trim(); // Retire espaces début/fin
str.trimStart(); // Début seulement
str.trimEnd(); // Fin seulement
str.padStart(10, '0'); // '000Hello World'
str.padEnd(15, '.'); // 'Hello World....'
str.repeat(3); // 'Hello WorldHello WorldHello World'
str.replace('World', 'JS'); // 'Hello JS'
str.replaceAll('o', '0'); // 'Hell0 W0rld'

// Split/Join
str.split(' '); // ['Hello', 'World']
str.split(''); // ['H', 'e', 'l', ...]
['Hello', 'World'].join(' '); // 'Hello World'

// Match
str.match(/[A-Z]/g); // ['H', 'W']
str.matchAll(/[a-z]/g); // Iterator

// Template literals
const name = 'Jean';
const age = 30;
const message = ${name} a ${age} ans;
const multiline = `
    Ligne 1
    Ligne 2
`;

// Tagged templates
function highlight(strings, ...values) {
    return strings.reduce((acc, str, i) => {
        return acc + str + (values[i] ? ${values[i]} : '');
    }, '');
}
const html = highlightLe prix est ${price} euros;

Number methods

// Parsing
parseInt('42'); // 42
parseInt('42px'); // 42
parseInt('1010', 2); // 10 (binaire)
parseFloat('3.14'); // 3.14
Number('42'); // 42
Number('42px'); // NaN

// Vérification
Number.isNaN(NaN); // true
Number.isFinite(42); // true
Number.isInteger(42); // true
Number.isSafeInteger(42); // true

// Formatage
const num = 1234.567;
num.toFixed(2); // '1234.57'
num.toPrecision(4); // '1235'
num.toExponential(2); // '1.23e+3'
num.toLocaleString('fr-FR'); // '1 234,567'
num.toLocaleString('fr-FR', {
    style: 'currency',
    currency: 'EUR'
}); // '1 234,57 €'

// Constantes
Number.MAXVALUE;
Number.MINVALUE;
Number.MAXSAFEINTEGER; // 2^53 - 1
Number.MINSAFEINTEGER;
Number.POSITIVEINFINITY;
Number.NEGATIVEINFINITY;
Number.EPSILON;

// Math
Math.abs(-5); // 5
Math.ceil(4.3); // 5
Math.floor(4.7); // 4
Math.round(4.5); // 5
Math.trunc(4.7); // 4 (retire décimales)
Math.max(1, 5, 3); // 5
Math.min(1, 5, 3); // 1
Math.pow(2, 3); // 8
Math.sqrt(16); // 4
Math.random(); // 0-1
Math.PI;
Math.E;

Date et Time

// Création
new Date(); // Maintenant
new Date('2024-12-18');
new Date('2024-12-18T10:30:00');
new Date(2024, 11, 18); // 18 décembre 2024 (mois 0-11)
new Date(timestamp);

// Timestamp
Date.now(); // Millisecondes depuis 1970
date.getTime();

// Get
date.getFullYear(); // 2024
date.getMonth(); // 0-11 (11 = décembre)
date.getDate(); // 1-31
date.getDay(); // 0-6 (0 = dimanche)
date.getHours(); // 0-23
date.getMinutes(); // 0-59
date.getSeconds(); // 0-59
date.getMilliseconds(); // 0-999

// Set
date.setFullYear(2025);
date.setMonth(11);
date.setDate(25);
date.setHours(12);

// Format
date.toString(); // "Wed Dec 18 2024 10:30:00 GMT+0100"
date.toISOString(); // "2024-12-18T09:30:00.000Z"
date.toLocaleDateString('fr-FR'); // "18/12/2024"
date.toLocaleTimeString('fr-FR'); // "10:30:00"
date.toLocaleString('fr-FR'); // "18/12/2024 10:30:00"

// Intl.DateTimeFormat (moderne)
const formatter = new Intl.DateTimeFormat('fr-FR', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit'
});
formatter.format(date); // "18 décembre 2024 à 10:30"

// Différence
const diff = date2 - date1; // Millisecondes
const days = diff / (1000  60  60  24);

Regex (expressions régulières)

// Création
const regex1 = /pattern/flags;
const regex2 = new RegExp('pattern', 'flags');

// Flags
// g - global (tous matches)
// i - insensible casse
// m - multiline
// s - dotAll (. match newline)
// u - unicode
// y - sticky

// Test
/hello/i.test('Hello World'); // true

// Match
'Hello World'.match(/[A-Z]/g); // ['H', 'W']
'Hello World'.match(/(w+) (w+)/); // ['Hello World', 'Hello', 'World']

// MatchAll
const matches = 'test1 test2 test3'.matchAll(/test(d)/g);
for (const match of matches) {
    console.log(match[0], match[1]);
}

// Replace
'Hello World'.replace(/World/, 'JS'); // 'Hello JS'
'test1 test2'.replace(/test(d)/g, 'item$1'); // 'item1 item2'
'test'.replace(/t/g, (match) => match.toUpperCase()); // 'TesT'

// Split
'a,b,c'.split(/,/); // ['a', 'b', 'c']

// Patterns utiles
/^d+$/; // Nombres uniquement
/^[a-zA-Z]+$/; // Lettres uniquement
/^[w-.]+@([w-]+.)+[w-]{2,4}$/; // Email basique
/^https?:///; // URL
/^d{5}$/; // Code postal 5 chiffres
/^0[1-9]d{8}$/; // Téléphone français

Bonnes pratiques

Performance

// Debounce - limite appels rapides
function debounce(func, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => func.apply(this, args), delay);
    };
}

const handleResize = debounce(() => {
    console.log('Window resized');
}, 250);
window.addEventListener('resize', handleResize);

// Throttle - max un appel par période
function throttle(func, limit) {
    let inThrottle;
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}

const handleScroll = throttle(() => {
    console.log('Scrolling');
}, 100);
window.addEventListener('scroll', handleScroll);

// Memoization
function memoize(fn) {
    const cache = new Map();
    return function(...args) {
        const key = JSON.stringify(args);
        if (cache.has(key)) {
            return cache.get(key);
        }
        const result = fn.apply(this, args);
        cache.set(key, result);
        return result;
    };
}

const fibonacci = memoize((n) => {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
});

Patterns modernes

// Optional chaining avec méthodes
obj?.method?.();
arr?.[0]?.property;

// Nullish coalescing pour defaults
const config = {
    timeout: userConfig?.timeout ?? 5000,
    retries: userConfig?.retries ?? 3
};

// Object shorthand
const name = 'Jean';
const age = 30;
const user = { name, age };

// Destructuring avec defaults
function greet({ name = 'Guest', greeting = 'Hello' } = {}) {
    return ${greeting}, ${name}!;
}

// Array methods chaining
users
    .filter(u => u.active)
    .map(u => u.name)
    .sort()
    .slice(0, 10);

// Async iteration
for await (const item of asyncIterator) {
    console.log(item);
}

---

Version: ES2024 (ECMAScript 2024) | Compatibilité: Navigateurs modernes

Une remarque, un retour ?

Cet article est vivant — corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.