Introduction
JavaScript a considérablement évolué depuis ES6 (ECMAScript 2015). Comprendre ces nouvelles fonctionnalités est essentiel pour travailler avec les frameworks modernes comme React, Vue ou Angular. Cet article vous guidera à travers les trois piliers du JavaScript moderne.
1. Variables : let, const, et la fin de var
Pourquoi éviter var ?
// ❌ Problème avec var : hoisting et scope de fonction
function exempleVar() {
console.log(count); // undefined (pas d'erreur!)
var count = 5;
if (true) {
var count = 10; // Même variable!
console.log(count); // 10
}
console.log(count); // 10 (modifié dans le if)
}
Utilisez let pour les variables mutables
// ✅ let : block scope et pas de hoisting
function exempleLet() {
// console.log(count); // ReferenceError!
let count = 5;
if (true) {
let count = 10; // Variable différente
console.log(count); // 10
}
console.log(count); // 5 (non affecté)
}
Utilisez const pour les constantes
// ✅ const : impossible de réassigner
const APIURL = 'https://api.example.com';
// APIURL = 'autre'; // TypeError!
// ⚠️ Les objets/arrays sont mutables
const user = { name: 'Alice' };
user.name = 'Bob'; // ✅ OK
user.age = 30; // ✅ OK
// user = {}; // ❌ TypeError!
const numbers = [1, 2, 3];
numbers.push(4); // ✅ OK
// numbers = []; // ❌ TypeError!
Règle d’or : Préférez const, utilisez let si nécessaire
// ✅ Bonne pratique
const maxUsers = 100;
const users = [];
let currentPage = 1;
function loadUsers() {
currentPage++; // let permet la modification
users.push(...fetchUsers(currentPage)); // const permet mutation
}
2. Fonctions fléchées (Arrow Functions)
Syntaxe de base
// Fonction traditionnelle
function add(a, b) {
return a + b;
}
// Arrow function équivalente
const add = (a, b) => {
return a + b;
};
// Return implicite (sans accolades)
const add = (a, b) => a + b;
// Un seul paramètre : parenthèses optionnelles
const square = x => x x;
// Pas de paramètres : parenthèses obligatoires
const getRandom = () => Math.random();
Exemples pratiques avec les arrays
const products = [
{ name: 'Laptop', price: 999 },
{ name: 'Mouse', price: 29 },
{ name: 'Keyboard', price: 79 }
];
// ❌ Ancienne méthode
const names = products.map(function(product) {
return product.name;
});
// ✅ Méthode moderne
const names = products.map(product => product.name);
// Filtrage et transformation
const expensiveProducts = products
.filter(p => p.price > 50)
.map(p => ({ ...p, discount: p.price 0.1 }));
// Réduction
const total = products.reduce((sum, p) => sum + p.price, 0);
console.log(total); // 1107
Différence cruciale : le contexte this
// ❌ Problème avec fonction traditionnelle
class Timer {
constructor() {
this.seconds = 0;
}
startOld() {
setInterval(function() {
this.seconds++; // ❌ this = window!
console.log(this.seconds); // NaN
}, 1000);
}
}
// ✅ Solution avec arrow function
class Timer {
constructor() {
this.seconds = 0;
}
start() {
setInterval(() => {
this.seconds++; // ✅ this = instance Timer
console.log(this.seconds); // 1, 2, 3...
}, 1000);
}
}
Cas d’usage réels
// Event handlers en React
const Button = () => {
const [count, setCount] = useState(0);
// ✅ Arrow function conserve le contexte
const handleClick = () => {
setCount(count + 1);
};
return ;
};
// API calls
const fetchUser = async (userId) => {
const response = await fetch(/api/users/${userId});
return response.json();
};
// Promesses chainées
fetchUser(123)
.then(user => user.posts)
.then(posts => posts.filter(p => p.published))
.catch(error => console.error(error));
3. Destructuring : Extraire des valeurs proprement
Destructuring d’objets
// ❌ Ancienne méthode
const user = {
firstName: 'Marie',
lastName: 'Dubois',
age: 28,
email: 'marie@example.com'
};
const firstName = user.firstName;
const lastName = user.lastName;
const email = user.email;
// ✅ Destructuring
const { firstName, lastName, email } = user;
// Renommer les variables
const { firstName: prenom, lastName: nom } = user;
console.log(prenom); // 'Marie'
// Valeurs par défaut
const { age, country = 'France' } = user;
console.log(country); // 'France'
// Destructuring imbriqué
const employee = {
name: 'Pierre',
position: {
title: 'Developer',
level: 'Senior',
department: 'Engineering'
}
};
const { position: { title, level } } = employee;
console.log(title); // 'Developer'
Destructuring dans les paramètres de fonction
// ✅ API moderne et lisible
const createUser = ({ name, email, role = 'user' }) => {
return {
id: Date.now(),
name,
email,
role,
createdAt: new Date()
};
};
const newUser = createUser({
name: 'Sophie',
email: 'sophie@example.com'
});
// Destructuring avec rest operator
const updateUser = ({ id, ...updates }) => {
return fetch(/api/users/${id}, {
method: 'PATCH',
body: JSON.stringify(updates)
});
};
updateUser({ id: 123, name: 'New Name', age: 30 });
Destructuring d’arrays
// Bases
const colors = ['red', 'green', 'blue', 'yellow'];
const [primary, secondary] = colors;
console.log(primary); // 'red'
console.log(secondary); // 'green'
// Ignorer des éléments
const [first, , third] = colors;
console.log(third); // 'blue'
// Rest operator
const [head, ...tail] = colors;
console.log(tail); // ['green', 'blue', 'yellow']
// Swap de variables
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1
// Avec des fonctions retournant des arrays
const useState = (initial) => {
let state = initial;
const setState = (newState) => { state = newState; };
return [state, setState];
};
const [count, setCount] = useState(0);
Cas pratiques : React Hooks
// useState
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// useEffect avec cleanup
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch('/api/data', {
signal: controller.signal
});
const data = await response.json();
setUser(data);
} catch (error) {
setError(error.message);
} finally {
setLoading(false);
}
};
fetchData();
return () => controller.abort();
}, []);
4. Combinaison des trois concepts
Exemple réel : Composant React moderne
// UserCard.jsx
import { useState, useEffect } from 'react';
const UserCard = ({ userId, theme = 'light' }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
try {
const response = await fetch(/api/users/${userId});
const data = await response.json();
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return Chargement...;
if (error) return Erreur: {error};
if (!user) return null;
const { name, email, avatar, stats = {} } = user;
const { posts = 0, followers = 0 } = stats;
return (
user-card user-card--${theme}}>
## {name}
{email}
{posts} posts
{followers} followers
);
};
export default UserCard;
Exemple TypeScript
// types.ts
interface User {
id: number;
name: string;
email: string;
avatar?: string;
stats?: {
posts: number;
followers: number;
};
}
interface UserCardProps {
userId: number;
theme?: 'light' | 'dark';
}
// UserCard.tsx
import { useState, useEffect } from 'react';
import type { User, UserCardProps } from './types';
const UserCard = ({ userId, theme = 'light' }: UserCardProps) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async (): Promise => {
try {
const response = await fetch(/api/users/${userId});
if (!response.ok) throw new Error('Failed to fetch');
const data: User = await response.json();
setUser(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return Chargement...;
if (error) return Erreur: {error};
if (!user) return null;
const { name, email, avatar, stats = {} } = user;
const { posts = 0, followers = 0 } = stats;
return (
user-card user-card--${theme}}>
{avatar &&
}
## {name}
{email}
{posts} posts
{followers} followers
);
};
export default UserCard;
5. Tests avec Jest
// utils.test.js
describe('Destructuring et Arrow Functions', () => {
test('extraction de propriétés d'objet', () => {
const user = { name: 'Alice', age: 25 };
const { name, age } = user;
expect(name).toBe('Alice');
expect(age).toBe(25);
});
test('valeurs par défaut', () => {
const { country = 'France' } = {};
expect(country).toBe('France');
});
test('arrow functions avec map', () => {
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n 2);
expect(doubled).toEqual([2, 4, 6]);
});
test('rest operator', () => {
const [first, ...rest] = [1, 2, 3, 4];
expect(first).toBe(1);
expect(rest).toEqual([2, 3, 4]);
});
});
// UserCard.test.jsx
import { render, screen, waitFor } from '@testing-library/react';
import UserCard from './UserCard';
describe('UserCard', () => {
beforeEach(() => {
global.fetch = jest.fn();
});
test('affiche le loading initial', () => {
render( );
expect(screen.getByText('Chargement...')).toBeInTheDocument();
});
test('affiche les données utilisateur', async () => {
const mockUser = {
id: 1,
name: 'Marie',
email: 'marie@example.com',
stats: { posts: 10, followers: 100 }
};
global.fetch.mockResolvedValueOnce({
ok: true,
json: async () => mockUser
});
render( );
await waitFor(() => {
expect(screen.getByText('Marie')).toBeInTheDocument();
expect(screen.getByText('marie@example.com')).toBeInTheDocument();
expect(screen.getByText('10 posts')).toBeInTheDocument();
});
});
test('gère les erreurs API', async () => {
global.fetch.mockRejectedValueOnce(new Error('Network error'));
render( );
await waitFor(() => {
expect(screen.getByText(/Erreur/)).toBeInTheDocument();
});
});
});
6. Benchmarks de performance
// performance-test.js
const iterations = 1000000;
// Test 1: let vs const (performance identique)
console.time('let');
for (let i = 0; i < iterations; i++) {
let x = i 2;
}
console.timeEnd('let'); // ~5ms
console.time('const');
for (let i = 0; i < iterations; i++) {
const x = i 2;
}
console.timeEnd('const'); // ~5ms
// Test 2: Arrow function vs regular function
const regularFunc = function(x) { return x 2; };
const arrowFunc = x => x * 2;
console.time('regular function');
for (let i = 0; i < iterations; i++) {
regularFunc(i);
}
console.timeEnd('regular function'); // ~8ms
console.time('arrow function');
for (let i = 0; i < iterations; i++) {
arrowFunc(i);
}
console.timeEnd('arrow function'); // ~7ms (légèrement plus rapide)
// Test 3: Destructuring vs propriété directe
const obj = { a: 1, b: 2, c: 3, d: 4 };
console.time('direct access');
for (let i = 0; i < iterations; i++) {
const a = obj.a;
const b = obj.b;
}
console.timeEnd('direct access'); // ~3ms
console.time('destructuring');
for (let i = 0; i < iterations; i++) {
const { a, b } = obj;
}
console.timeEnd('destructuring'); // ~4ms (légèrement plus lent, négligeable)
7. Meilleures pratiques 2025
Configuration ESLint
{
"env": {
"es2024": true,
"browser": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"no-var": "error",
"prefer-const": "error",
"prefer-arrow-callback": "error",
"prefer-destructuring": ["error", {
"array": true,
"object": true
}],
"arrow-body-style": ["error", "as-needed"]
}
}
Checklist de code review
- [ ] Aucun
varutilisé - [ ]
constpar défaut,letseulement si mutation nécessaire - [ ] Arrow functions pour les callbacks et méthodes courtes
- [ ] Destructuring pour extraire plusieurs propriétés
- [ ] Rest/spread operators au lieu de
Object.assignouArray.concat - [ ] Types TypeScript définis pour les interfaces publiques
- Variables : Toujours
const, parfoislet, jamaisvar - Arrow functions : Syntaxe concise + contexte
thispréservé - Destructuring : Code plus propre et intentions claires
- Pratiquez sur JavaScript.info
- Explorez les autres features ES6+ : modules, classes, async/await
- Passez à l'article suivant : "Introduction à React"
- MDN Web Docs - JavaScript
- TypeScript Handbook
- ESLint Rules
Conclusion
Maîtriser ces trois concepts fondamentaux vous permettra d'écrire du JavaScript moderne, lisible et performant. Ces patterns sont utilisés partout dans l'écosystème React, Next.js, et tous les frameworks modernes.