Javascript 4


Ces exercices sont ceux de la séance : ils sont intercalés dans les slides, juste après le concept qu’ils font travailler. Cette page les rassemble pour que vous puissiez les reprendre à votre rythme.

Les six premiers exercices sont courts : ils font manipuler une syntaxe chacun. Les sept suivants portent tous sur l’immutabilité, et montent en difficulté — c’est la notion qui compte le plus pour la suite du cours, parce que React repose entièrement dessus.

La documentation de Javascript est disponible ici ou là.

Exercice 1 — Des valeurs par défaut

Facile ⏱ 4 min

🎯 Voir ce qui déclenche — ou non — la valeur par défaut.

Écrire createUser(name, role) où role vaut "user" par défaut.

function createUser(name, role) {
  // à vous
}

console.log(createUser("Alice"))              // role: "user"
console.log(createUser("Bob", "admin"))       // role: "admin"
console.log(createUser("Carl", null))         // et là ?
console.log(createUser("Dora", undefined))    // et là ?

Les deux derniers appels ne donnent pas le même résultat. Pourquoi ?

Exercice 2 — Naviguer dans un objet incertain

Intermédiaire ⏱ 8 min

🎯 Choisir entre `||` et `??` en connaissance de cause.

Afficher la ville de chaque utilisateur, ou "Ville inconnue" — sans qu’aucun accès ne plante.

const users = [
  { name: "Alice", address: { city: "Paris" } },
  { name: "Bob", address: null },
  { name: "Carl" },
]

for (const user of users) {
  // ?. et ??
}

Puis, ce piège :

const settings = { volume: 0, label: "" }

console.log(settings.volume || 50)   // ?
console.log(settings.volume ?? 50)   // ?

0 et "" sont falsy mais pas nullish : c’est toute la différence entre les deux opérateurs.

Exercice 3 — Déstructurer plutôt que répéter

Facile ⏱ 6 min

🎯 Extraire ce dont on a besoin, en une ligne.

Réécrire cette fonction en déstructurant son paramètre, avec une valeur par défaut pour city.

function describe(user) {
  return `${user.firstname} ${user.lastname}, ${user.city ?? "ville inconnue"}`
}

console.log(describe({ firstname: "Alice", lastname: "Smith", city: "Paris" }))
console.log(describe({ firstname: "Bob", lastname: "Jones" }))

Bonus : récupérer le premier élément et tout le reste d’un tableau en une seule ligne.

Exercice 4 — Copier en changeant une chose

Intermédiaire ⏱ 8 min

🎯 Le geste que vous ferez le plus souvent en React.

Sans jamais modifier l’original :

const user = { id: 1, name: "Alice", role: "user" }
const numbers = [2, 3]

// 1. un nouvel utilisateur avec role: "admin"
// 2. le même, plus une propriété `active: true`
// 3. [1, 2, 3, 4] à partir de `numbers`
// 4. fusionner { a: 1 } et { b: 2 }

console.log(user) // doit être intact à la fin

Puis expliquez la différence entre { ...obj, b: 1 } et { b: 1, ...obj }. L’ordre compte.

Exercice 5 — Accepter n'importe quel nombre d'arguments

Intermédiaire ⏱ 6 min

🎯 Distinguer le rest (au paramètre) du spread (à l'appel).

Écrire max(...numbers) qui retourne le plus grand, et 0 si on ne lui passe rien.

function max(/* à vous */) {
}

console.log(max(3, 9, 2))   // 9
console.log(max())          // 0

const values = [4, 7, 1]
console.log(max(...values))  // 7 — ici c'est du spread, pas du rest

Même symbole ..., deux rôles opposés : rassembler à la déclaration, étaler à l’appel.

Exercice 6 — Construire une phrase

Facile ⏱ 4 min

🎯 Arrêter de concaténer avec des `+`.

Réécrire cet affichage avec un template string, sur plusieurs lignes et sans un seul +.

const user = { firstname: "Alice", lastname: "Smith", age: 25 }

console.log(
  "Bonjour " + user.firstname + " " + user.lastname +
  ",\nvous avez " + user.age + " ans." +
  "\nL'an prochain vous en aurez " + (user.age + 1) + "."
)

Un template string accepte n’importe quelle expression entre ${}, pas seulement une variable.

Exercice 7 — Mutation cachée

Intermédiaire ⏱ 10 min

🎯 Repérer une fonction qui modifie ce qu'on lui prête.

addTag a l’air inoffensive. Elle ne l’est pas.

const users = [
  { id: 1, name: "Alice", tags: ["admin"] },
  { id: 2, name: "Bob", tags: ["user"] },
]

function addTag(user, tag) {
  user.tags.push(tag)
  return user
}

const updated = addTag(users[0], "editor")
console.log(users[0].tags)          // 😱
console.log(updated === users[0])   // 😱

Réécrivez-la pour qu’elle retourne un nouvel utilisateur sans jamais toucher l’original, puis vérifiez avec === que l’utilisateur et son tableau tags sont bien nouveaux.

Exercice 8 — Une copie qui n'en est pas une

Difficile ⏱ 8 min

🎯 Savoir lire une affectation entre objets.

const state = { counter: 0 }

function increment(state) {
  const newState = state
  newState.counter++
  return newState
}

const next = increment(state)
console.log(next === state)   // 😱
console.log(state.counter)    // 😱
  1. Décrivez ce qui se passe en mémoire à la ligne const newState = state.
  2. Réécrivez increment correctement.
  3. Écrivez deux console.log qui prouvent l’immutabilité de votre version.

const ne crée pas de copie. Il ne fait qu’interdire de réaffecter le nom.

Exercice 9 — Retirer sans splice

Intermédiaire ⏱ 8 min

🎯 Remplacer une méthode mutante par une méthode qui retourne du neuf.

splice modifie le tableau reçu. Réécrivez cette fonction sans elle.

const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Carl" },
]

function removeUserById(users, id) {
  const index = users.findIndex((u) => u.id === id)
  users.splice(index, 1)
  return users
}

console.log(removeUserById(users, 2))
console.log(users) // 😱 l'original a perdu Bob

💡 filter retourne un nouveau tableau.

Que fait la version d’origine si l’id n’existe pas ? (findIndex renvoie -1…)

Exercice 10 — Incrémenter sans modifier

Intermédiaire ⏱ 6 min

🎯 Copier un objet en changeant un champ, sans le recopier à la main.

const player = { name: "Alice", score: 10, level: 3 }

function incrementScore(player) {
  player.score += 1
  return player
}

console.log(incrementScore(player))
console.log(player) // 😱

Réécrivez incrementScore : player ne doit jamais changer, et sans énumérer name et level un par un.

Exercice 11 — Immutabilité profonde

Intermédiaire ⏱ 12 min

🎯 Comprendre qu'une copie de surface ne suffit pas.

Écrire updateLastname(state, newLastname) qui ne modifie rien et clone tous les niveaux nécessaires.

const state = {
  user: { id: 1, profile: { firstname: "Julien", lastname: "Usson" } },
}

function updateLastname(state, newLastname) {
  // à vous
}

const next = updateLastname(state, "Dupont")
console.log(state !== next)                            // true
console.log(state.user !== next.user)                  // true
console.log(state.user.profile !== next.user.profile)  // true
console.log(state.user.profile.lastname)               // "Usson"

Essayez d’abord avec un seul niveau de spread : quelle assertion tombe ?

Exercice 12 — Le piège de la référence partagée

Difficile ⏱ 10 min

🎯 Voir qu'un objet déclaré une fois est partagé par tous ceux qui le pointent.

const defaultSettings = { theme: "dark", notifications: true }

function createUser(name) {
  return { name, settings: defaultSettings }
}

const user1 = createUser("Alice")
const user2 = createUser("Bob")

user1.settings.theme = "light"
console.log(user2.settings.theme) // 😱
  1. Expliquez précisément pourquoi user2 est touché.
  2. Corrigez createUser.
  3. Vérifiez que user1.settings !== user2.settings.

const empêche de réaffecter la variable, pas de modifier l’objet qu’elle désigne.

Exercice 13 — Ne recréer que ce qui change

Difficile ⏱ 12 min

🎯 Produire un nouvel état minimal — exactement ce qu'attend React.

Écrire setUserOnline(state, userId) : un nouvel état, un seul utilisateur passe online: true, et les autres gardent leur référence d’origine.

const state = {
  users: [
    { id: 1, name: "Alice", online: false },
    { id: 2, name: "Bob", online: false },
  ],
}

function setUserOnline(state, userId) {
  // à vous
}

const next = setUserOnline(state, 1)
console.log(next.users[0].online)                  // true
console.log(next.users[1] === state.users[1])      // true — Bob n'a pas bougé
console.log(next !== state)                        // true

Ce dernier point n’est pas un détail : c’est ce qui permet à React de ne pas réafficher Bob.