JS Basics Flashcards

(98 cards)

1
Q

Qu’est-ce qu’une closure ?

A

Une closure est une fonction qui a accès aux variables de sa portée lexicale externe, même après que la fonction externe ait terminé son exécution.

Définition simple : Fonction + Environnement lexical sauvegardé = Closure

Exemple :

function outer() {
  const message = 'Hello';
  return function inner() {
    console.log(message); // inner capture message
  };
}
const myFunction = outer();
myFunction(); // 'Hello' - message existe encore !
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Quel est le mnémonique pour les closures ?

A

C.L.O.S.E

  • Capture des variables
  • Lexical scope (portée lexicale)
  • Outer function retourne inner function
  • Sauvegarde du contexte
  • Encapsulation des données
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Comment résoudre le problème classique des closures dans les boucles avec var ?

A

Problème :

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Affiche : 3, 3, 3 ❌

Solutions :

  1. Utiliser let (block scope) :
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Affiche : 0, 1, 2 ✓
  1. IIFE (Immediately Invoked Function Expression) :
for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(() => console.log(j), 100);
  })(i);
}
  1. Closure explicite :
function createLogger(value) {
  return function() {
    console.log(value);
  };
}
for (var i = 0; i < 3; i++) {
  setTimeout(createLogger(i), 100);
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Citez 5 cas d’usage pratiques des closures

A
  1. Données privées / Encapsulation
    • Créer des variables privées inaccessibles de l’extérieur
  2. Factory functions
    • Créer des fonctions personnalisées avec configuration
  3. Event handlers avec contexte
    • Maintenir le contexte dans les callbacks
  4. Memoization / Cache
    • Stocker des résultats de calculs coûteux
  5. Module pattern
    • Créer des modules avec API publique et données privées
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Créez un compteur avec données privées utilisant les closures

A
function createCounter() {
  let count = 0; // Variable PRIVÉE

  return {
    increment() {
      count++;
      return count;
    },
    decrement() {
      count--;
      return count;
    },
    getCount() {
      return count;
    }
  };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount();  // 2

// ❌ Impossible d'accéder directement
console.log(counter.count); // undefined
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Quel est le piège avec les closures et la mémoire ?

A

Les closures gardent des références aux variables externes en mémoire, ce qui peut empêcher le garbage collection si mal utilisé.

Problème :

function createHugeArray() {
  const hugeArray = new Array(1000000).fill('data');

  return function() {
    console.log('Fonction créée');
  };
}

const fn = createHugeArray();
// hugeArray reste en mémoire tant que fn existe !

Solution :

// Ne pas capturer si pas nécessaire
// Ou libérer explicitement :
let fn = createHugeArray();
fn = null; // Permet le garbage collection
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Les closures gardent-elles une copie ou une référence des variables ?

A

Une RÉFÉRENCE, pas une copie.

Si la variable change, la closure voit le changement.

Exemple :

function createFunctions() {
  const functions = [];
  for (var i = 0; i < 3; i++) {
    functions.push(function() {
      return i; // Référence à i
    });
  }
  return functions;
}

const fns = createFunctions();
fns[0](); // 3 (pas 0 !)
fns[1](); // 3 (pas 1 !)
fns[2](); // 3

Toutes les fonctions référencent la MÊME variable i.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Créez une factory function pour multiplier par un facteur

A
function createMultiplier(factor) {
  return function(number) {
    return number * factor;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);
const quadruple = createMultiplier(4);

console.log(double(5));      // 10
console.log(triple(5));      // 15
console.log(quadruple(5));   // 20

Chaque fonction retournée a sa propre closure avec son propre ‘factor’.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Implémentez une fonction ‘once’ utilisant les closures

A
function once(fn) {
  let called = false;
  let result;

  return function(...args) {
    if (!called) {
      called = true;
      result = fn(...args);
    }
    return result;
  };
}

// Usage
function initialize() {
  console.log('Initializing...');
  return 'Initialized';
}

const initOnce = once(initialize);

console.log(initOnce()); // 'Initializing...' → 'Initialized'
console.log(initOnce()); // 'Initialized' (pas de log)
console.log(initOnce()); // 'Initialized' (pas de log)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Créez une fonction de memoization avec closures

A
function memoize(fn) {
  const cache = {}; // Capturé par closure

  return function(...args) {
    const key = JSON.stringify(args);

    if (key in cache) {
      console.log('Depuis le cache');
      return cache[key];
    }

    console.log('Calcul...');
    const result = fn(...args);
    cache[key] = result;
    return result;
  };
}

// Usage
function slowSquare(n) {
  return n * n;
}

const memoizedSquare = memoize(slowSquare);

console.log(memoizedSquare(5)); // 'Calcul...' → 25
console.log(memoizedSquare(5)); // 'Depuis le cache' → 25
console.log(memoizedSquare(6)); // 'Calcul...' → 36
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Quelle est la différence entre scope et closure ?

A

Scope (Portée) :
- Règles qui déterminent OÙ les variables sont accessibles
- Déterminé par la structure du code

Closure (Fermeture) :
- Fonction + son environnement lexical sauvegardé
- Permet d’accéder aux variables même après que la fonction externe ait terminé

Relation :
Les closures utilisent le lexical scope pour capturer et maintenir l’accès aux variables.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Pourquoi les closures existent-elles en JavaScript ?

A

JavaScript est garbage-collecté : les variables inutilisées sont automatiquement supprimées.

Sans closure :

function create() {
  const data = 'Important';
}
create();
// data est détruit par le garbage collector

Avec closure :

function create() {
  const data = 'Important';
  return function() {
    console.log(data);
  };
}

const fn = create();
// data N'EST PAS détruit car fn y a encore accès !
fn(); // 'Important'

Le garbage collector garde ‘data’ en mémoire tant que fn existe.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Qu’est-ce que ‘this’ en JavaScript ?

A

‘this’ est un mot-clé qui référence un objet, mais quel objet dépend de comment la fonction est appelée.

Règle d’or : ‘this’ est déterminé à l’exécution (runtime), pas à la définition.

Exemple :

function greet() {
  console.log(this.name);
}

const person = { name: 'Alice' };
greet(); // undefined (this = global)
greet.call(person); // 'Alice' (this = person)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Quelles sont les 4 règles de binding pour ‘this’ (par ordre de priorité) ?

A

1. NEW binding (Priorité la plus haute)
- Appelée avec ‘new’, this = nouvel objet

2. Explicit binding
- call/apply/bind, this = objet passé

3. Implicit binding
- objet.méthode(), this = objet

4. Default binding (Priorité la plus basse)
- this = window/global (ou undefined en strict mode)

Ordre : new > explicit > implicit > default

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Quel est le mnémonique pour ‘this’ ?

A

B.I.N.D

  • Bind/call/apply pour forcer
  • Invocation context (comment on appelle)
  • New keyword crée nouveau contexte
  • Default = window/global (ou undefined en strict)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Que se passe-t-il dans ce code ?

const obj = {
  name: 'Alice',
  greet() { console.log(this.name); }
};
const greet = obj.greet;
greet();
A

Résultat : ‘undefined’ (strict mode) ou ‘[nom de window.name]’ (non-strict)

Explication :
La méthode perd son contexte quand elle est assignée à une variable. ‘this’ devient l’objet global/undefined car il n’y a plus de binding implicite.

Solutions :

// 1. Arrow function
const greet = () => obj.greet();

// 2. bind
const greet = obj.greet.bind(obj);

// 3. Appeler directement
obj.greet();
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Les arrow functions ont-elles leur propre ‘this’ ?

A

Non. Les arrow functions n’ont PAS leur propre ‘this’.

Elles héritent du ‘this’ de leur contexte lexical (où elles sont définies).

Conséquences :
- Elles ignorent call/apply/bind
- Elles ne peuvent pas être utilisées comme constructeurs

Exemple :

const obj = {
  name: 'Alice',
  regular: function() {
    console.log(this.name); // 'Alice'
  },
  arrow: () => {
    console.log(this.name); // undefined (this global)
  }
};

obj.regular(); // 'Alice'
obj.arrow();   // undefined
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Comment fixer le problème de ‘this’ dans setTimeout ?

A

Problème :

const person = {
  name: 'Alice',
  greet() {
    setTimeout(function() {
      console.log(this.name); // undefined ❌
    }, 1000);
  }
};

Solutions :

1. Arrow function (Recommandé) :

setTimeout(() => {
  console.log(this.name); // 'Alice' ✓
}, 1000);

2. bind :

setTimeout(function() {
  console.log(this.name);
}.bind(this), 1000);

3. Variable ‘that’ :

const that = this;
setTimeout(function() {
  console.log(that.name); // 'Alice' ✓
}, 1000);
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Différence entre call, apply et bind ?

A

call(thisArg, arg1, arg2, …) :
- Invoque IMMÉDIATEMENT
- Arguments séparés par virgules

apply(thisArg, [args]) :
- Invoque IMMÉDIATEMENT
- Arguments en tableau

bind(thisArg, arg1, …) :
- N’invoque PAS
- Retourne une NOUVELLE fonction avec ‘this’ fixé

Mnémonique :
- Call : Comma-separated, Calls now
- Apply : Array, Also calls now
- Bind : Binds, But doesn’t call

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Que vaut ‘this’ avec le new keyword ?

A

Avec ‘new’, JavaScript crée un nouveau objet et ‘this’ pointe vers cet objet.

Exemple :

function User(name) {
  this.name = name;
}

const user = new User('Alice');
console.log(user.name); // 'Alice'

Ce que fait ‘new’ en coulisses :

function User(name) {
  // 1. const this = {};
  // 2. this.\_\_proto\_\_ = User.prototype;

  this.name = name;

  // 3. return this;
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Que vaut ‘this’ dans une arrow function au niveau global ?

A

Le ‘this’ du contexte parent (généralement ‘window’ en navigateur, ou l’objet global en Node.js).

Exemple :

const greet = () => {
  console.log(this);
};

greet(); // window (ou global en Node.js)

Important :

const obj = {
  value: 42,
  getValue: () => this.value
};

obj.getValue(); // undefined
// L'arrow function hérite du this global, pas de obj !
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Fixez ce code :

const person = {
  name: 'Bob',
  friends: ['Alice', 'Charlie'],
  showFriends() {
    this.friends.forEach(function(friend) {
      console.log(this.name + ' knows ' + friend);
    });
  }
};
A

Solutions :

1. Arrow function (Recommandé) :

showFriends() {
  this.friends.forEach(friend => {
    console.log(this.name + ' knows ' + friend);
  });
}

2. bind :

showFriends() {
  this.friends.forEach(function(friend) {
    console.log(this.name + ' knows ' + friend);
  }.bind(this));
}

3. thisArg (forEach accepte un 2e paramètre) :

showFriends() {
  this.friends.forEach(function(friend) {
    console.log(this.name + ' knows ' + friend);
  }, this); // ← thisArg
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Peut-on changer le ‘this’ d’une arrow function avec bind ?

A

Non. Le ‘this’ d’une arrow function est fixé à sa création (lexical binding) et ne peut PAS être changé.

Exemple :

const greet = () => {
  console.log(this.name);
};

const person = { name: 'Alice' };

greet.call(person);   // undefined (ignoré!)
greet.apply(person);  // undefined (ignoré!)
const bound = greet.bind(person);
bound();              // undefined (ignoré!)

Les arrow functions ignorent complètement call, apply et bind pour ‘this’.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

Comment créer une calculatrice chaînée avec ‘this’ ?

A
const calculator = {
  value: 0,

  add(n) {
    this.value += n;
    return this; // Retourne l'objet pour chaînage
  },

  subtract(n) {
    this.value -= n;
    return this;
  },

  multiply(n) {
    this.value *= n;
    return this;
  },

  divide(n) {
    this.value /= n;
    return this;
  },

  result() {
    return this.value;
  }
};

const result = calculator
  .add(5)
  .multiply(3)
  .add(2)
  .result();

console.log(result); // 17
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Qu'est-ce qu'une Promise ?
Une **Promise** est un objet représentant la **complétion ou l'échec éventuel** d'une opération asynchrone et sa valeur résultante. **Analogie :** Le reçu de livraison Quand vous commandez en ligne, vous recevez un reçu (Promise). Le reçu promet que vous recevrez votre commande plus tard. Le colis peut arriver (resolved) ou être perdu (rejected). **Exemple :** ``` const promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Terminé!'); }, 1000); }); promise.then(result => console.log(result)); ```
26
Quels sont les 3 états d'une Promise ?
**1. Pending (En attente)** - État initial - L'opération n'est ni terminée ni échouée **2. Fulfilled (Résolue)** - L'opération s'est terminée avec succès - Une valeur est disponible **3. Rejected (Rejetée)** - L'opération a échoué - Une raison d'erreur est disponible **Important :** Une Promise ne peut changer d'état qu'**une seule fois**. Une fois settled (fulfilled ou rejected), son état est **immuable**.
27
Quel est le mnémonique pour les Promises ?
**P.R.O.M.I.S.E** - **P**ending (en attente) - **R**esolved/Rejected (terminé) - **O**bjet avec then/catch - **M**icrotask queue - **I**mmutable une fois settled - **S**tates: pending → fulfilled/rejected - **E**rror handling avec catch
28
Comment créer une Promise ?
**Syntaxe :** ``` const promise = new Promise((resolve, reject) => { // Code asynchrone if (/* succès */) { resolve(valeur); // Promise fulfilled } else { reject(erreur); // Promise rejected } }); ``` **Exemple :** ``` const promise = new Promise((resolve, reject) => { setTimeout(() => { const success = true; if (success) { resolve('Opération réussie!'); } else { reject('Erreur!'); } }, 1000); }); promise .then(result => console.log(result)) .catch(error => console.error(error)); ```
29
Différence entre Promise.all et Promise.allSettled ?
**Promise.all :** - Rejette dès qu'**une seule** Promise échoue - Retourne un tableau de valeurs si toutes réussissent - Utile quand toutes les opérations DOIVENT réussir ``` Promise.all([p1, p2, p3]) .then(results => console.log(results)) // [val1, val2, val3] .catch(err => console.error(err)); // Si UNE échoue ``` **Promise.allSettled :** - Attend que **toutes** soient terminées (fulfilled ou rejected) - Retourne toujours un tableau d'objets {status, value/reason} - Utile quand vous voulez le résultat de toutes ``` Promise.allSettled([p1, p2, p3]) .then(results => { // [ // { status: 'fulfilled', value: ... }, // { status: 'rejected', reason: ... }, // ... // ] }); ```
30
Que fait Promise.race ?
Promise.race retourne la **PREMIÈRE** Promise qui se termine (fulfilled ou rejected), et ignore les autres. **Exemple :** ``` const slow = new Promise(resolve => setTimeout(() => resolve('Lent'), 2000) ); const fast = new Promise(resolve => setTimeout(() => resolve('Rapide'), 100) ); Promise.race([slow, fast]) .then(result => console.log(result)); // 'Rapide' ``` **Cas d'usage :** - Timeout pour requêtes - Choisir le serveur le plus rapide ``` function fetchWithTimeout(url, timeout = 5000) { return Promise.race([ fetch(url), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout) ) ]); } ```
31
Que fait Promise.any (ES2021) ?
Promise.any retourne la **première** Promise **fulfilled**. Rejette si **toutes** échouent. **Exemple :** ``` const promises = [ Promise.reject('Erreur 1'), Promise.resolve('Succès!'), Promise.reject('Erreur 2') ]; Promise.any(promises) .then(result => console.log(result)); // 'Succès!' // Si toutes échouent const allFail = [ Promise.reject('Erreur 1'), Promise.reject('Erreur 2') ]; Promise.any(allFail) .catch(error => console.error(error)); // AggregateError ```
32
Quel est l'ordre d'exécution ? ``` console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4'); ```
**Résultat :** 1, 4, 3, 2 **Explication :** 1. **'1'** : Code synchrone (Call Stack) 2. `setTimeout` : Enregistré dans Macrotask Queue 3. `Promise.then` : Enregistré dans Microtask Queue 4. **'4'** : Code synchrone (Call Stack) 5. Call Stack vide → Microtasks exécutées → **'3'** 6. Microtasks vides → Une Macrotask → **'2'** **Ordre Event Loop :** Synchrone → Microtasks (Promises) → Macrotasks (setTimeout)
33
Quel est le piège commun avec forEach et les Promises ?
**Problème :** ``` async function processItems(items) { items.forEach(async item => { await processItem(item); // forEach n'attend PAS ! }); console.log('Terminé'); // Affiché AVANT le traitement ! } ``` forEach n'attend pas les promises. Le code continue immédiatement. **Solutions :** **1. for...of** (séquentiel) : ``` for (const item of items) { await processItem(item); } console.log('Terminé'); // Affiché APRÈS ``` **2. Promise.all** (parallèle) : ``` await Promise.all(items.map(item => processItem(item))); console.log('Terminé'); // Affiché APRÈS ```
34
Comment gérer les erreurs dans les Promises ?
**Avec .catch() :** ``` fetch('/api/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Erreur:', error)); ``` **Propagation d'erreurs :** ``` Promise.resolve(5) .then(value => value * 2) .then(value => { throw new Error('Problème!'); }) .then(value => { console.log('Ne sera pas appelé'); }) .catch(error => { console.error('Attrapé:', error.message); }); ``` **Recovery après erreur :** ``` Promise.reject('Erreur') .catch(error => { console.log('Erreur attrapée:', error); return 'Valeur de récupération'; }) .then(value => { console.log('Continué avec:', value); }); ```
35
Créez une fonction retry avec Promises
``` function retry(fn, maxAttempts = 3) { return fn().catch(error => { if (maxAttempts <= 1) { throw error; } console.log(`Réessai... (${maxAttempts - 1} restantes)`); return retry(fn, maxAttempts - 1); }); } // Usage retry(() => fetch('/api/data')) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Échec final:', error)); ```
36
Créez une fonction timeout avec Promises
``` function withTimeout(promise, ms) { const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms) ); return Promise.race([promise, timeout]); } // Usage withTimeout(fetch('/api/slow'), 5000) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error)); // 'Timeout' après 5s ```
37
Quel est le piège avec le wrapping inutile de Promises ?
**❌ MAUVAIS** (Promise Wrapping inutile) : ``` function getData() { return new Promise((resolve, reject) => { fetch('/api/data') .then(response => resolve(response)) .catch(error => reject(error)); }); } ``` **✅ BON** (fetch retourne déjà une Promise) : ``` function getData() { return fetch('/api/data'); } ``` **Règle :** Ne créez pas de nouvelle Promise si vous en avez déjà une !
38
Qu'est-ce qu'async/await ?
**Async/await** est une syntaxe qui rend le code asynchrone **plus lisible** en le faisant ressembler à du code synchrone. - **async** : Déclare une fonction asynchrone (retourne toujours une Promise) - **await** : Pause l'exécution jusqu'à ce que la Promise soit résolue **Exemple :** ``` async function getData() { const response = await fetch('/api/data'); const data = await response.json(); return data; // Retourne automatiquement une Promise } ```
39
Qu'est-ce qu'une fonction async ?
Une fonction déclarée avec 'async' qui retourne **TOUJOURS une Promise**. Si la fonction retourne une valeur, elle est automatiquement wrappée dans Promise.resolve(). **Exemple :** ``` async function test() { return 42; } test().then(value => console.log(value)); // 42 // Équivalent à : function test() { return Promise.resolve(42); } ```
40
Peut-on utiliser await en dehors d'une fonction async ?
**Non** dans une fonction normale. **Oui** au niveau supérieur dans un module ES (Top-Level Await, ES2022). **Exemple :** ``` // ❌ Erreur function test() { await fetch('/api'); // SyntaxError } // ✅ OK dans une fonction async async function test() { await fetch('/api'); } // ✅ OK au niveau supérieur (module ES) // main.js const data = await fetch('/api'); ```
41
Différence entre exécution séquentielle et parallèle ?
**Code A - Séquentiel** (lent) : ``` async function getData() { const x = await fetch('/1'); // Attend const y = await fetch('/2'); // Attend // Temps total = T1 + T2 } ``` **Code B - Parallèle** (rapide) : ``` async function getData() { const [x, y] = await Promise.all([ fetch('/1'), fetch('/2') ]); // Temps total = max(T1, T2) } ``` **Règle :** Utilisez Promise.all si les opérations sont indépendantes !
42
Comment gérer les erreurs avec async/await ?
**Utiliser try/catch :** ``` async function getData() { try { const response = await fetch('/api/data'); const data = await response.json(); return data; } catch (error) { console.error('Erreur:', error); throw error; // Ou gérer autrement } finally { console.log('Requête terminée'); // Toujours exécuté } } ``` **Alternative - sans try/catch :** ``` async function getData() { const response = await fetch('/api/data'); return await response.json(); } // Gestion d'erreur à l'appel getData() .then(data => console.log(data)) .catch(error => console.error(error)); ```
43
Quel est le problème avec forEach et async/await ?
**❌ Problème :** ``` async function processItems(items) { items.forEach(async item => { await processItem(item); // forEach n'attend PAS }); console.log('Terminé'); // Affiché AVANT le traitement } ``` forEach ne comprend pas async/await et n'attend pas les promises. **✅ Solutions :** **1. for...of** (séquentiel) : ``` for (const item of items) { await processItem(item); } console.log('Terminé'); // Affiché APRÈS ``` **2. Promise.all** (parallèle) : ``` await Promise.all(items.map(item => processItem(item))); console.log('Terminé'); // Affiché APRÈS ```
44
Que va afficher ce code ? ``` async function test() { console.log('1'); await Promise.resolve(); console.log('2'); } console.log('3'); test(); console.log('4'); ```
**Résultat :** 3, 1, 4, 2 **Explication :** 1. **'3'** : Code synchrone 2. Appel test() : **'1'** 3. await met le reste dans microtask queue 4. **'4'** : Code synchrone 5. Microtask : **'2'** **Flow :** - Sync : 3, 1, 4 - Microtask (après await) : 2
45
Comment exécuter des opérations en parallèle avec async/await ?
**❌ Séquentiel (lent) :** ``` async function getAll() { const user = await fetchUser(); // Attend const posts = await fetchPosts(); // Attend const comments = await fetchComments(); // Attend // Temps total : T1 + T2 + T3 } ``` **✅ Parallèle avec Promise.all :** ``` async function getAll() { const [user, posts, comments] = await Promise.all([ fetchUser(), fetchPosts(), fetchComments() ]); // Temps total : max(T1, T2, T3) } ``` **✅ Démarrage parallèle manuel :** ``` async function getAll() { // Démarrer toutes les requêtes en parallèle const userPromise = fetchUser(); const postsPromise = fetchPosts(); const commentsPromise = fetchComments(); // Attendre les résultats const user = await userPromise; const posts = await postsPromise; const comments = await commentsPromise; } ```
46
Créez une fonction retry avec async/await
``` async function retry(fn, maxAttempts = 3, delay = 1000) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { if (attempt === maxAttempts) { throw error; } console.log(`Tentative ${attempt} échouée, réessai dans ${delay}ms...`); await new Promise(resolve => setTimeout(resolve, delay)); } } } // Usage const data = await retry(() => fetch('/api/data')); ```
47
Créez une fonction timeout avec async/await
``` function timeout(ms) { return new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms) ); } async function fetchWithTimeout(url, ms = 5000) { return Promise.race([ fetch(url), timeout(ms) ]); } // Usage try { const response = await fetchWithTimeout('/api/slow', 3000); const data = await response.json(); console.log(data); } catch (error) { console.error('Timeout ou erreur:', error); } ```
48
Quel est le piège avec return sans await ?
**❌ Erreur non capturée :** ``` async function getData() { try { return fetch('/api/data'); // Oubli de await } catch (error) { console.error(error); // Ne capturera JAMAIS l'erreur ! } } ``` Sans await, la Promise est retournée immédiatement sans être résolue. Si elle rejette, l'erreur n'est pas capturée par le try/catch. **✅ Erreur capturée :** ``` async function getData() { try { return await fetch('/api/data'); } catch (error) { console.error(error); // Capture l'erreur } } ```
49
Comment traiter un batch d'items avec async/await ?
``` async function processInBatches(items, batchSize = 5) { const results = []; for (let i = 0; i < items.length; i += batchSize) { const batch = items.slice(i, i + batchSize); const batchResults = await Promise.all( batch.map(item => processItem(item)) ); results.push(...batchResults); } return results; } // Traite 100 items, 5 à la fois const results = await processInBatches(items, 5); ```
50
Qu'est-ce que l'Event Loop ?
L'**Event Loop** est le mécanisme qui permet à JavaScript (single-threaded) d'exécuter du code asynchrone. Il surveille le Call Stack et les Task Queues, exécutant les tâches dans l'ordre : **Algorithme :** 1. Exécuter le code synchrone (Call Stack) 2. Quand le Call Stack est vide : a. Exécuter TOUTES les Microtasks b. Exécuter UNE Macrotask c. Répéter **Analogie du Restaurant :** - Chef (Call Stack) : cuisine une commande à la fois - Serveur (Event Loop) : apporte les plats quand le chef est disponible - Commandes VIP (Microtasks) : servies en premier - Commandes normales (Macrotasks) : servies après les VIP
51
Quel est le mnémonique pour l'Event Loop ?
**R.E.S.T.O** (Le Restaurant) - **R**equest (Call Stack) - **E**n attente (Task Queue) - **S**erveur rapide (Microtasks) - **T**âches (Macrotasks) - **O**rdre d'exécution
52
Qu'est-ce que la Call Stack ?
La **Call Stack** (Pile d'Exécution) est une structure **LIFO** (Last In, First Out) qui garde trace des fonctions en cours d'exécution. **Exemple :** ``` function third() { console.log('3'); } function second() { third(); } function first() { second(); } first(); ``` **Call Stack :** ``` │ third() │ ← Exécute et pop │ second() │ ← Appelle third │ first() │ ← Appelle second │ main │ ← Appelle first └──────────┘ ```
53
Différence entre Microtasks et Macrotasks ?
**Microtasks :** - **Priorité HAUTE** - **TOUTES** exécutées avant la prochaine macrotask - Exemples : Promise.then, queueMicrotask, MutationObserver **Macrotasks :** - **Priorité BASSE** - **UNE SEULE** par cycle d'Event Loop - Exemples : setTimeout, setInterval, I/O, UI rendering **Tableau :** | Aspect | Microtasks | Macrotasks | |--------|------------|------------| | Priorité | ✅ Haute | ⬇️ Basse | | Exécution | Toutes | Une seule | | Bloquant | Oui (si trop) | Non |
54
Donnez 3 exemples de Microtasks
1. **Promise.then/catch/finally** ``` Promise.resolve().then(() => {}); ``` 2. **queueMicrotask()** ``` queueMicrotask(() => {}); ``` 3. **MutationObserver** ``` const observer = new MutationObserver(() => {}); ``` 4. **process.nextTick()** (Node.js - encore plus prioritaire) ``` process.nextTick(() => {}); ```
55
Donnez 3 exemples de Macrotasks
1. **setTimeout / setInterval** ``` setTimeout(() => {}, 0); setInterval(() => {}, 100); ``` 2. **I/O operations** - Lecture de fichiers - Requêtes réseau 3. **UI rendering events** - requestAnimationFrame - UI updates 4. **setImmediate** (Node.js uniquement) ``` setImmediate(() => {}); ```
56
Quel est l'ordre d'exécution ? ``` console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C')); queueMicrotask(() => console.log('D')); console.log('E'); ```
**Résultat :** A, E, C, D, B **Explication détaillée :** 1. **'A'** : Code synchrone (Call Stack) 2. `setTimeout` : Enregistré dans Macrotask Queue 3. `Promise.then` : Enregistré dans Microtask Queue 4. `queueMicrotask` : Enregistré dans Microtask Queue 5. **'E'** : Code synchrone (Call Stack) 6. Call Stack vide → Microtasks : **'C', 'D'** 7. Microtasks vides → Une Macrotask : **'B'** **Ordre Event Loop :** Synchrone → Toutes Microtasks → Une Macrotask
57
Pourquoi ce code peut bloquer ? ``` function loop() { Promise.resolve().then(loop); } loop(); setTimeout(() => console.log('Timeout'), 0); ```
**Résultat :** 'Timeout' ne s'exécute JAMAIS ! **Pourquoi ?** La fonction loop() ajoute constamment de nouvelles microtasks. L'Event Loop exécute **TOUTES** les microtasks avant les macrotasks. Si on ajoute constamment des microtasks, les macrotasks ne sont jamais traitées. C'est le **'microtask starvation'**. **Visualisation :** ``` loop() → Promise.then(loop) → loop() → Promise.then(loop) → ... ↑______________________________________________| Les microtasks ne finissent jamais → setTimeout n'est jamais exécuté ```
58
Quel est l'ordre de priorité de l'Event Loop ?
**Ordre de priorité (du plus haut au plus bas) :** ``` 1. Call Stack (Code synchrone) ↓ 2. Microtasks (TOUTES) - Promise.then/catch/finally - queueMicrotask() - MutationObserver ↓ 3. Macrotask (UNE SEULE) - setTimeout - setInterval - I/O ↓ 4. Retour à l'étape 2 (Event Loop) ``` **Règle :** Synchrone > Toutes Microtasks > Une Macrotask
59
Ordre d'exécution dans Node.js avec process.nextTick ?
Dans **Node.js**, process.nextTick a la **plus haute priorité** : ``` console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); process.nextTick(() => console.log('4')); console.log('5'); ``` **Résultat :** 1, 5, 4, 3, 2 **Ordre Node.js :** 1. Code synchrone (1, 5) 2. process.nextTick (4) 3. Microtasks/Promises (3) 4. Macrotasks/setTimeout (2)
60
Que se passe-t-il si on ajoute des microtasks dans une microtask ?
Les nouvelles microtasks sont **ajoutées à la queue** et seront exécutées **avant** la prochaine macrotask. **Exemple :** ``` Promise.resolve() .then(() => { console.log('1'); Promise.resolve().then(() => console.log('2')); }) .then(() => console.log('3')); setTimeout(() => console.log('4'), 0); ``` **Résultat :** 1, 2, 3, 4 **Explication :** 1. Première microtask : '1' + ajoute nouvelle microtask 2. Deuxième microtask : '2' 3. Troisième microtask : '3' 4. Toutes microtasks terminées → Macrotask : '4'
61
Comment débugger l'Event Loop ?
**1. Console.log avec timestamp :** ``` function log(msg) { console.log(`[${performance.now().toFixed(2)}ms] ${msg}`); } log('Start'); setTimeout(() => log('Timeout'), 0); Promise.resolve().then(() => log('Promise')); log('End'); ``` **2. Chrome DevTools - Performance Tab :** - Ouvrir DevTools - Onglet Performance - Enregistrer - Voir les Tasks, Microtasks, etc. **3. Visualiser la queue :** ``` console.log('Call Stack:', 'Message'); queueMicrotask(() => console.log('Microtask:', 'Message')); setTimeout(() => console.log('Macrotask:', 'Message'), 0); ```
62
Qu'est-ce que le hoisting ?
Le **hoisting** est un mécanisme JavaScript où les **DÉCLARATIONS** de variables et fonctions sont 'remontées' (**hoisted**) au début de leur scope avant l'exécution du code. **Important :** Seules les **déclarations** montent, pas les **initialisations**. **Exemple :** ``` // Ce que vous écrivez console.log(name); // undefined var name = 'Alice'; // Ce que JavaScript voit var name; // Déclaration hoistée console.log(name); // undefined name = 'Alice'; // Initialisation reste en place ```
63
Quel est le mnémonique pour le hoisting ?
**L.I.F.T** - **L**es déclarations montent - **I**nitialisations restent en place - **F**unctions montent complètement - **T**DZ pour let/const
64
Différence de hoisting entre var, let et const ?
**var :** - Hoisté au début du scope - Initialisé à 'undefined' - Accessible avant déclaration (vaut undefined) ``` console.log(x); // undefined var x = 5; ``` **let/const :** - Hoisté au début du bloc - PAS initialisé (TDZ - Temporal Dead Zone) - ReferenceError si accès avant déclaration ``` console.log(y); // ReferenceError let y = 10; console.log(z); // ReferenceError const z = 15; ```
65
Qu'est-ce que la Temporal Dead Zone (TDZ) ?
La **TDZ** (Temporal Dead Zone) est la zone entre le début du scope et la déclaration où la variable existe mais n'est PAS accessible. **Exemple :** ``` { // TDZ commence ici ⬇️ console.log(name); // ❌ ReferenceError // TDZ continue... let name = 'Alice'; // TDZ se termine ici ⬆️ console.log(name); // ✅ 'Alice' } ``` **Pourquoi la TDZ existe ?** 1. Détecter les erreurs (accès avant déclaration) 2. Const safety (const ne peut pas être undefined puis initialisé)
66
Différence entre function declaration et function expression pour le hoisting ?
**Function Declaration :** - **Toute la fonction** est hoistée - Peut être appelée AVANT sa déclaration ``` foo(); // ✅ 'Function' function foo() { console.log('Function'); } ``` **Function Expression :** - Seule la **variable** est hoistée (undefined) - TypeError si appelée avant ``` bar(); // ❌ TypeError: bar is not a function var bar = function() { console.log('Expression'); }; ```
67
Que va afficher ce code ? ``` console.log(a); var a = 10; console.log(a); ```
**Résultat :** ``` undefined 10 ``` **Explication :** JavaScript voit le code comme : ``` var a; // Déclaration hoistée, initialisée à undefined console.log(a); // undefined a = 10; // Assignment reste en place console.log(a); // 10 ```
68
Que va afficher ce code ? ``` var foo = 1; function bar() { console.log(foo); var foo = 2; console.log(foo); } bar(); ```
**Résultat :** ``` undefined 2 ``` **Explication :** JavaScript voit bar() comme : ``` function bar() { var foo; // Hoisté au début de la fonction console.log(foo); // undefined foo = 2; console.log(foo); // 2 } ``` La variable locale 'foo' **masque** la variable globale (shadowing).
69
Quel est le problème avec var dans les boucles ?
**Problème :** ``` for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); } // Affiche : 3, 3, 3 ❌ ``` 'var' n'a pas de block scope. Il n'y a qu'**une seule variable i** partagée. **Solution avec let :** ``` for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); } // Affiche : 0, 1, 2 ✓ ``` 'let' crée un **nouveau scope** à chaque itération.
70
Que se passe-t-il avec function hoisting priority ?
Quand une function declaration et une variable ont le même nom, la **function declaration** est hoistée en premier. **Exemple :** ``` foo(); // 'Function' var foo = function() { console.log('Variable'); }; function foo() { console.log('Function'); } foo(); // 'Variable' ``` **Ce que JavaScript voit :** ``` // 1. Function declaration montée en premier function foo() { console.log('Function'); } // 2. Variable declaration montée (ne réécrit pas) var foo; foo(); // 'Function' // 3. Assignment exécuté foo = function() { console.log('Variable'); }; foo(); // 'Variable' ```
71
Comment éviter les problèmes de hoisting ?
**Bonnes pratiques :** **1. Utilisez const par défaut** ``` const API_URL = 'https://api.example.com'; const config = { timeout: 5000 }; ``` **2. let si réassignation nécessaire** ``` let counter = 0; counter++; ``` **3. Évitez var** (sauf legacy code) **4. Déclarez les variables en haut du scope** ``` function test() { const name = 'Alice'; let count = 0; // Reste du code... } ``` **5. Utilisez strict mode** ``` 'use strict'; x = 10; // ReferenceError (sans var/let/const) ```
72
Piège avec typeof et TDZ ?
**Avant ES6 :** ``` console.log(typeof undeclaredVariable); // 'undefined' ✓ ``` typeof ne lançait jamais d'erreur. **Avec let/const (TDZ) :** ``` console.log(typeof undeclaredVariable); // 'undefined' ✓ console.log(typeof declaredVariable); // ❌ ReferenceError let declaredVariable; ``` Si la variable est dans la TDZ, typeof lance une erreur !
73
Problème avec hoisting et switch ?
**Problème :** ``` switch (value) { case 1: let name = 'Alice'; // ❌ Erreur si case 2 break; case 2: let name = 'Bob'; // SyntaxError: redeclaration break; } ``` Tous les cases partagent le même scope. **Solution - Créer des blocs :** ``` switch (value) { case 1: { let name = 'Alice'; // ✓ Nouveau scope break; } case 2: { let name = 'Bob'; // ✓ Nouveau scope break; } } ```
74
Qu'est-ce qu'un callback ?
Un **callback** est une **fonction passée en argument** à une autre fonction, qui sera **exécutée plus tard** (après un événement, une opération async, etc.). **Analogie :** C'est comme donner votre numéro de téléphone en disant 'Rappelle-moi quand tu as fini'. **Exemple :** ``` function doSomething(callback) { // Faire quelque chose callback(); // Exécuter le callback } function myCallback() { console.log('Callback exécuté!'); } doSomething(myCallback); // Passer la fonction (sans ()) ```
75
Différence entre callback synchrone et asynchrone ?
**Callback Synchrone :** - Exécuté **immédiatement** dans le même call stack - Exemples : forEach, map, filter ``` [1, 2, 3].forEach(n => console.log(n)); // Sync ``` **Callback Asynchrone :** - Exécuté **plus tard**, après une opération async - Exemples : setTimeout, addEventListener, fetch ``` setTimeout(() => console.log('Async'), 0); // Async ```
76
Qu'est-ce que le callback hell ?
Le **callback hell** (pyramide de la mort) est une situation où les callbacks sont imbriqués profondément, rendant le code difficile à lire et maintenir. **Problème :** ``` getData(a => { getData2(a, b => { getData3(b, c => { getData4(c, d => { getData5(d, e => { console.log(e); }); }); }); }); }); ``` **Problèmes :** - Difficile à lire (pyramide) - Gestion d'erreur complexe - Difficile à débugger - Code pas réutilisable
77
Quelles sont les solutions au callback hell ?
**Solution 1 : Named Functions** ``` function handleUser(err, user) { if (err) return handleError(err); getPostsByUser(user.id, handlePosts); } function handlePosts(err, posts) { if (err) return handleError(err); console.log(posts); } getUserById(userId, handleUser); ``` **Solution 2 : Promises** ``` getUserById(userId) .then(user => getPostsByUser(user.id)) .then(posts => console.log(posts)) .catch(err => console.error(err)); ``` **Solution 3 : Async/Await** ``` async function loadData() { try { const user = await getUserById(userId); const posts = await getPostsByUser(user.id); console.log(posts); } catch (err) { console.error(err); } } ```
78
Qu'est-ce que le error-first callback pattern ?
**Convention Node.js** où le premier paramètre du callback est toujours l'erreur (null si succès). **Pattern :** ``` function readFileAsync(filename, callback) { // callback(error, result) // Si erreur : callback(error, null) // Si succès : callback(null, result) } ``` **Usage :** ``` fs.readFile('file.txt', (err, data) => { if (err) { console.error('Erreur:', err); return; // Important : return pour arrêter } console.log('Données:', data); }); ``` **Avantages :** - Cohérence dans tout Node.js - Force la gestion d'erreur - Clair si erreur ou succès
79
Comment éviter de perdre le contexte 'this' dans un callback ?
**Problème :** ``` const obj = { name: 'Alice', greet() { console.log(`Hello, ${this.name}`); } }; setTimeout(obj.greet, 1000); // 'Hello, undefined' ❌ ``` **Solutions :** **1. Arrow function** (Recommandé) : ``` setTimeout(() => obj.greet(), 1000); // ✅ ``` **2. bind** : ``` setTimeout(obj.greet.bind(obj), 1000); // ✅ ``` **3. Variable that** : ``` const that = this; setTimeout(function() { that.greet(); }, 1000); // ✅ ```
80
Exemples de callbacks dans les Array Methods
**forEach :** ``` [1, 2, 3].forEach((num, index) => { console.log(`Index ${index}: ${num}`); }); ``` **map :** ``` const doubled = [1, 2, 3].map(n => n * 2); // [2, 4, 6] ``` **filter :** ``` const evens = [1, 2, 3, 4, 5, 6].filter(n => n % 2 === 0); // [2, 4, 6] ``` **reduce :** ``` const sum = [1, 2, 3, 4].reduce((acc, n) => acc + n, 0); // 10 ``` Tous ces méthodes prennent une fonction **callback**.
81
Comment créer une fonction avec callback pour opération asynchrone ?
``` function delay(ms, callback) { setTimeout(callback, ms); } // Usage delay(1000, function() { console.log('1 seconde écoulée'); }); // Avec données function fetchUser(id, callback) { setTimeout(function() { const user = { id: id, name: 'Alice' }; callback(null, user); // Error-first }, 1000); } // Usage fetchUser(1, function(err, user) { if (err) { console.error(err); return; } console.log('User:', user); }); ```
82
Quel est le piège avec l'appel multiple d'un callback ?
**Problème :** ``` function getData(callback) { callback('first'); callback('second'); // ❌ Appelé 2 fois ! } ``` Un callback ne devrait être appelé qu'**une seule fois**. **Solution - Safe Callback :** ``` function getData(callback) { let called = false; function safeCallback(...args) { if (called) return; called = true; callback(...args); } // Utiliser safeCallback safeCallback('first'); safeCallback('second'); // Ignoré } ```
83
Piège : callback parfois sync, parfois async
**❌ TRÈS MAUVAIS :** ``` function maybeSync(callback) { if (cache) { callback(cache); // Synchrone } else { fetchData((data) => { callback(data); // Asynchrone }); } } ``` Parfois sync, parfois async = comportement imprévisible. **✅ Toujours cohérent (async) :** ``` function alwaysAsync(callback) { if (cache) { // Forcer async même si donnée en cache process.nextTick(() => callback(cache)); // ou setTimeout(() => callback(cache), 0); } else { fetchData(callback); } } ```
84
Créez une fonction retry avec callbacks
``` function retryOperation(operation, maxRetries, callback) { let attempts = 0; function attempt() { attempts++; operation((err, result) => { if (err && attempts < maxRetries) { console.log(`Tentative ${attempts} échouée, réessai...`); attempt(); } else { callback(err, result); } }); } attempt(); } // Usage function unreliableOperation(callback) { if (Math.random() < 0.7) { callback(new Error('Échec aléatoire')); } else { callback(null, 'Succès!'); } } retryOperation(unreliableOperation, 5, (err, result) => { if (err) { console.error('Échec final:', err); } else { console.log('Résultat:', result); } }); ```
85
Créez une queue de callbacks
``` class CallbackQueue { constructor() { this.queue = []; } add(callback) { this.queue.push(callback); } execute() { this.queue.forEach(callback => callback()); this.queue = []; // Vider la queue } } // Usage const queue = new CallbackQueue(); queue.add(() => console.log('1')); queue.add(() => console.log('2')); queue.add(() => console.log('3')); queue.execute(); // '1', '2', '3' ```
86
Qu'est-ce que call() ?
**call()** est une méthode qui permet d'appeler une fonction en spécifiant explicitement : 1. La valeur de 'this' 2. Les arguments (séparés par virgules) Elle **EXÉCUTE IMMÉDIATEMENT** la fonction. **Syntaxe :** ``` function.call(thisArg, arg1, arg2, ...) ``` **Exemple :** ``` function greet(greeting) { return `${greeting}, ${this.name}`; } const person = { name: 'Alice' }; greet.call(person, 'Hello'); // 'Hello, Alice' ```
87
Qu'est-ce qu'apply() ?
**apply()** est une méthode qui permet d'appeler une fonction en spécifiant : 1. La valeur de 'this' 2. Les arguments **en tableau** Elle **EXÉCUTE IMMÉDIATEMENT** la fonction. **Syntaxe :** ``` function.apply(thisArg, [args]) ``` **Exemple :** ``` function greet(greeting, punctuation) { return `${greeting}, ${this.name}${punctuation}`; } const person = { name: 'Alice' }; greet.apply(person, ['Hello', '!']); // 'Hello, Alice!' ```
88
Qu'est-ce que bind() ?
**bind()** est une méthode qui **crée une nouvelle fonction** avec : 1. Un contexte 'this' fixé 2. Des arguments pré-remplis (optional) Elle **N'EXÉCUTE PAS** la fonction, elle en **RETOURNE une nouvelle**. **Syntaxe :** ``` const newFunc = func.bind(thisArg, arg1, arg2, ...) ``` **Exemple :** ``` function greet(greeting) { return `${greeting}, ${this.name}`; } const person = { name: 'Alice' }; const boundGreet = greet.bind(person, 'Hello'); console.log(boundGreet); // [Function: bound greet] console.log(boundGreet()); // 'Hello, Alice' ```
89
Différence entre call, apply et bind ?
**Tableau comparatif :** | Méthode | Exécution | Arguments | Retour | |---------|-----------|-----------|--------| | **call** | ✅ Immédiate | Séparés (a, b, c) | Résultat | | **apply** | ✅ Immédiate | Tableau [a, b, c] | Résultat | | **bind** | ❌ Différée | Séparés (a, b, c) | Fonction | **Mnémonique : 'CAB'** - **C**all : **C**omma-separated, **C**alls now - **A**pply : **A**rray, **A**lso calls now - **B**ind : **B**inds, **B**ut doesn't call
90
Exemple côte à côte de call, apply et bind
``` function greet(greeting, punctuation) { return `${greeting}, ${this.name}${punctuation}`; } const person = { name: 'Alice' }; // CALL - Arguments séparés, exécution immédiate const r1 = greet.call(person, 'Hello', '!'); console.log(r1); // 'Hello, Alice!' // APPLY - Arguments en tableau, exécution immédiate const r2 = greet.apply(person, ['Hello', '!']); console.log(r2); // 'Hello, Alice!' // BIND - Arguments séparés, retourne fonction const boundGreet = greet.bind(person, 'Hello', '!'); console.log(boundGreet); // [Function: bound greet] console.log(boundGreet()); // 'Hello, Alice!' ```
91
Comment utiliser call pour emprunter des méthodes ?
``` const person1 = { name: 'Alice', greet() { console.log(`Bonjour, je suis ${this.name}`); } }; const person2 = { name: 'Bob' }; // Bob emprunte la méthode greet d'Alice person1.greet.call(person2); // 'Bonjour, je suis Bob' // Exemple avec Array.prototype const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 }; // Emprunter slice const arr = Array.prototype.slice.call(arrayLike); console.log(arr); // ['a', 'b', 'c'] ```
92
Comment utiliser call pour le chaînage de constructeurs ?
``` function Animal(name) { this.name = name; } function Dog(name, breed) { // Appeler le constructeur Animal Animal.call(this, name); this.breed = breed; } const dog = new Dog('Rex', 'Labrador'); console.log(dog.name); // 'Rex' (de Animal) console.log(dog.breed); // 'Labrador' (de Dog) // Chaînage multiple function Shape(color) { this.color = color; } function Rectangle(color, width, height) { Shape.call(this, color); this.width = width; this.height = height; } function Square(color, size) { Rectangle.call(this, color, size, size); } const square = new Square('red', 10); console.log(square.color); // 'red' console.log(square.width); // 10 console.log(square.height); // 10 ```
93
Comment vérifier le type exact d'une valeur avec call ?
``` function getType(value) { return Object.prototype.toString.call(value); } getType(42); // '[object Number]' getType('hello'); // '[object String]' getType([]); // '[object Array]' getType({}); // '[object Object]' getType(null); // '[object Null]' getType(undefined); // '[object Undefined]' getType(new Date()); // '[object Date]' getType(/regex/); // '[object RegExp]' // Pourquoi utiliser call ? // Parce que array.toString() peut être surchargé const arr = [1, 2, 3]; console.log(arr.toString()); // '1,2,3' (surchargé) console.log(Object.prototype.toString.call(arr)); // '[object Array]' ```
94
Comment trouver le max/min d'un tableau avec apply ?
``` const numbers = [5, 2, 9, 1, 7]; // ❌ PROBLÈME Math.max(numbers); // NaN (passe [5,2,9,1,7] comme 1 arg) // ✅ SOLUTION avec apply const max = Math.max.apply(null, numbers); // 9 const min = Math.min.apply(null, numbers); // 1 // ✅ MODERNE (ES6+) avec spread const max2 = Math.max(...numbers); // 9 const min2 = Math.min(...numbers); // 1 ```
95
Comment utiliser bind pour fixer 'this' perdu ?
**Problème - 'this' perdu :** ``` const person = { name: 'Alice', greet() { console.log(`Bonjour, je suis ${this.name}`); } }; const greet = person.greet; greet(); // 'Bonjour, je suis undefined' ❌ ``` **Solution avec bind :** ``` const person = { name: 'Alice', greet() { console.log(`Bonjour, je suis ${this.name}`); } }; const greet = person.greet.bind(person); greet(); // 'Bonjour, je suis Alice' ✅ ``` **Dans Event Listeners :** ``` class Button { constructor(label) { this.label = label; this.clickCount = 0; } handleClick() { this.clickCount++; console.log(`${this.label} cliqué ${this.clickCount} fois`); } init() { const btn = document.querySelector('#myButton'); // bind fixe 'this' à l'instance Button btn.addEventListener('click', this.handleClick.bind(this)); } } ```
96
Comment utiliser bind pour partial application ?
``` function multiply(a, b) { return a * b; } // Créer une fonction 'multiplier par 2' const double = multiply.bind(null, 2); // ↑ ↑ // this a=2 console.log(double(5)); // 10 console.log(double(10)); // 20 // Exemple plus complexe function log(level, message) { console.log(`[${level}] ${message}`); } // Créer des loggers spécialisés const info = log.bind(null, 'INFO'); const error = log.bind(null, 'ERROR'); const warning = log.bind(null, 'WARNING'); info('Application démarrée'); // [INFO] Application démarrée error('Erreur de connexion'); // [ERROR] Erreur de connexion warning('Mémoire faible'); // [WARNING] Mémoire faible ```
97
Peut-on re-binder une fonction déjà bindée ?
**Non.** Le premier bind fixe 'this' **DÉFINITIVEMENT**. ``` function greet() { return this.name; } const person1 = { name: 'Alice' }; const person2 = { name: 'Bob' }; const greet1 = greet.bind(person1); const greet2 = greet1.bind(person2); // Essaie de re-binder console.log(greet2()); // 'Alice' (pas 'Bob' !) ``` Impossible de changer le 'this' d'une fonction déjà bindée.
98
Les arrow functions réagissent-elles à call/apply/bind ?
**Non.** Les arrow functions IGNORENT call, apply et bind. Elles gardent le 'this' de leur **contexte lexical** et ne peuvent pas être changées. ``` const greet = () => { console.log(this.name); }; const person = { name: 'Alice' }; greet.call(person); // undefined (ignoré!) greet.apply(person); // undefined (ignoré!) const bound = greet.bind(person); bound(); // undefined (ignoré!) // Exemple dans un objet const obj = { name: 'Alice', regular: function() { console.log(this.name); // 'Alice' }, arrow: () => { console.log(this.name); // undefined } }; obj.regular.call({ name: 'Bob' }); // 'Bob' ✅ obj.arrow.call({ name: 'Bob' }); // undefined ❌ ```