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
🎯 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
🎯 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
🎯 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
🎯 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
🎯 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
🎯 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
🎯 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
🎯 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) // 😱
- Décrivez ce qui se passe en mémoire à la ligne
const newState = state. - Réécrivez
incrementcorrectement. - Écrivez deux
console.logqui 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
🎯 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
🎯 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
🎯 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
🎯 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) // 😱
- Expliquez précisément pourquoi
user2est touché. - Corrigez
createUser. - 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
🎯 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.