Debutant 8 min de lecture · 1 697 mots

JavaScript moderne (ES6+) : Variables, fonctions fléchées, et destructuring

Estimated reading time: 8 minutes

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} ## {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}} ## {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 var utilisé
  • [ ] const par défaut, let seulement 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.assign ou Array.concat
  • [ ] Types TypeScript définis pour les interfaces publiques
  • 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.

    Points clés à retenir

  • Variables : Toujours const, parfois let, jamais var
  • Arrow functions : Syntaxe concise + contexte this préservé
  • Destructuring : Code plus propre et intentions claires
  • Prochaines étapes

  • Pratiquez sur JavaScript.info
  • Explorez les autres features ES6+ : modules, classes, async/await
  • Passez à l'article suivant : "Introduction à React"
  • Ressources complémentaires

  • MDN Web Docs - JavaScript
  • TypeScript Handbook
  • ESLint Rules

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.