iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
Formations
JavaScript · Débutant

JavaScript

Apprendre JavaScript de zéro jusqu'au niveau expert : syntaxe moderne, DOM, asynchrone et event loop, modules, prototypes, métaprogrammation, performance et mémoire.

JavaScript ES2025Node.jsDOMVitestVite
01Introduction et environnement02Variables, types et coercition03Conditions et boucles04Fonctions, portée et closures05Objets, tableaux, Map et Set06Le DOM et les événements07Asynchrone : callbacks, promesses, async/await et event loop08Modules, npm, outillage et tests09Prototypes, classes et this10Itérateurs, générateurs, Symbol, Proxy et métaprogrammation11Niveau expert : moteur, performance, mémoire et architecture
Chapitre 8·45 min

Modules, npm, outillage et tests

Un projet JavaScript moderne ne se limite pas au langage. Il y a un système de modules, un gestionnaire de paquets, un bundler, un linter, un framework de test. Ce chapitre installe cet environnement et explique le rôle de chaque pièce, pour ne plus copier des configurations sans les comprendre.

Modules ES

Chaque fichier est un module avec sa propre portée. On exporte explicitement ce qui doit être visible.

math.js
export const PI = 3.14159
export function aire(r) {
  return PI * r ** 2
}
export default function perimetre(r) {
  return 2 * PI * r
}
main.js
import perimetre, { aire, PI as pi } from './math.js'
import * as math from './math.js'
 
console.log(aire(2), perimetre(2), math.PI)

Je préfère les exports nommés aux exports par défaut : l'autocomplétion fonctionne mieux et un renommage se propage partout.

Import dynamique

bouton.addEventListener('click', async () => {
  const { genererPDF } = await import('./pdf.js')   // chargé seulement au clic
  genererPDF(document.body)
})

C'est la base du code splitting : les bundlers créent un fichier séparé pour chaque import() dynamique.

Particularités des modules

  • mode strict automatique
  • this vaut undefined au niveau supérieur
  • exécutés une seule fois, même importés dix fois
  • les imports sont des liaisons vivantes : si le module exportateur modifie une variable exportée, les importateurs voient la nouvelle valeur

CommonJS

L'ancien système de Node, encore très présent :

const fs = require('node:fs')
module.exports = { lire }

Dans un nouveau projet, ajoutez "type": "module" dans package.json et utilisez partout import / export.

npm et package.json

npm init -y
npm install zod                   # dépendance de production
npm install -D vitest eslint      # dépendance de développement
npm uninstall zod
npm outdated
npm audit
package.json
{
  "name": "mon-projet",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "test": "vitest",
    "lint": "eslint ."
  },
  "dependencies": {
    "zod": "^4.0.0"
  },
  "devDependencies": {
    "vitest": "^4.0.0"
  }
}

Versions sémantiques

MAJEUR.MINEUR.CORRECTIF. Le préfixe ^4.0.0 accepte toute version 4.x.x supérieure ou égale : pas de changement majeur. ~4.0.0 n'accepte que les correctifs 4.0.x.

package-lock.json fige les versions exactes de tout l'arbre de dépendances. Il se committe. En CI, utilisez npm ci qui installe exactement le lockfile et échoue s'il est désynchronisé.

npx

npx create-vite@latest mon-app
npx serve dist

Exécute un paquet sans l'installer globalement.

Vite : serveur de dev et build

npm create vite@latest mon-app -- --template vanilla
cd mon-app
npm install
npm run dev

En développement, Vite sert les modules ES natifs directement au navigateur, sans bundler : le démarrage est instantané. En production, vite build produit des fichiers optimisés (minification, tree shaking, hash dans les noms pour le cache).

Le tree shaking élimine le code non importé. Il ne fonctionne qu'avec les modules ES, car leurs imports sont statiques et analysables.

Qualité : ESLint et Prettier

npm init @eslint/config@latest
eslint.config.js
import js from '@eslint/js'
import globals from 'globals'
 
export default [
  js.configs.recommended,
  {
    languageOptions: { globals: { ...globals.browser, ...globals.node } },
    rules: {
      eqeqeq: 'error',
      'no-var': 'error',
      'prefer-const': 'error',
    },
  },
]

ESLint trouve les bugs probables, Prettier gère la forme. Ne demandez pas à ESLint de faire du formatage.

TypeScript

TypeScript ajoute un système de types statiques à JavaScript, retiré à la compilation. Sur tout projet qui dépasse quelques centaines de lignes, je le considère comme indispensable. L'article TypeScript pour les développeurs JavaScript couvre la transition.

Sans passer à TypeScript, JSDoc donne déjà l'autocomplétion et la vérification dans VS Code :

// @ts-check
 
/**
 * @param {number} prix
 * @param {number} pourcentage
 * @returns {number}
 */
export function appliquerRemise(prix, pourcentage) {
  return Math.round(prix * (1 - pourcentage / 100) * 100) / 100
}

Tests avec Vitest

npm install -D vitest
src/remise.js
export function appliquerRemise(prix, pourcentage) {
  if (pourcentage < 0 || pourcentage > 100) throw new RangeError('Pourcentage invalide')
  return Math.round(prix * (1 - pourcentage / 100) * 100) / 100
}
src/remise.test.js
import { describe, it, expect } from 'vitest'
import { appliquerRemise } from './remise.js'
 
describe('appliquerRemise', () => {
  it('applique 20 %', () => {
    expect(appliquerRemise(100, 20)).toBe(80)
  })
 
  it('rejette un pourcentage invalide', () => {
    expect(() => appliquerRemise(100, 150)).toThrow(RangeError)
  })
 
  it.each([
    [100, 0, 100],
    [100, 100, 0],
    [19.99, 10, 17.99],
  ])('%s avec %s %% donne %s', (prix, pct, attendu) => {
    expect(appliquerRemise(prix, pct)).toBe(attendu)
  })
})
npx vitest          # mode watch
npx vitest run --coverage

Mocks et temps

import { vi, it, expect } from 'vitest'
 
it('appelle l\'API', async () => {
  const fetchMock = vi.fn().mockResolvedValue({ ok: true, json: async () => ({ id: 1 }) })
  vi.stubGlobal('fetch', fetchMock)
 
  const data = await getJSON('/api/user')
 
  expect(fetchMock).toHaveBeenCalledWith('/api/user', expect.any(Object))
  expect(data).toEqual({ id: 1 })
})
 
it('debounce attend 300 ms', () => {
  vi.useFakeTimers()
  const fn = vi.fn()
  const d = debounce(fn, 300)
  d(); d(); d()
  vi.advanceTimersByTime(299)
  expect(fn).not.toHaveBeenCalled()
  vi.advanceTimersByTime(1)
  expect(fn).toHaveBeenCalledOnce()
})

Le test runner natif de Node

Pour une petite bibliothèque, pas besoin de dépendance :

remise.test.js
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { appliquerRemise } from './remise.js'
 
test('applique 20 %', () => {
  assert.equal(appliquerRemise(100, 20), 80)
})
node --test

Les tests unitaires vérifient les fonctions isolées. Pour tester l'application entière dans un vrai navigateur, voir les tests end-to-end avec Playwright.

Exercice

Créer un projet Vite vanilla, y déplacer la todo list du chapitre 6 en séparant l'état (store.js), le rendu (render.js) et le point d'entrée (main.js). Écrire des tests Vitest pour les fonctions de store.js (ajouter, basculer, supprimer), qui doivent être pures.

src/store.js
export const ajouter = (todos, texte) => [...todos, { id: crypto.randomUUID(), texte, fait: false }]
export const basculer = (todos, id) => todos.map(t => (t.id === id ? { ...t, fait: !t.fait } : t))
export const supprimer = (todos, id) => todos.filter(t => t.id !== id)

Modules ES, lockfile committé, Vite, ESLint et Vitest : c'est la base commune de presque tous les projets JavaScript actuels. Avec cet outillage en place, on peut attaquer les mécanismes plus profonds du langage.

Précédent
Asynchrone : callbacks, promesses, async/await et event loop
Suivant
Prototypes, classes et this

Développeur fullstack passionné. J'apprends en construisant et je documente tout — front, back, outils. Le code s'apprend mieux en public.

Naviguer

IndexTous les articlesFormationsProfilOutilsBibliothech

Ailleurs

GitHub RSS

Newsletter

Les articles, libs et découvertes. Une fois par semaine, pas plus.

© 2026 William LoreeConçu & codé à la main