Maison > interface Web > js tutoriel > Fermetures dévoilées : exploration des royaumes cachés de JavaScript

Fermetures dévoilées : exploration des royaumes cachés de JavaScript

DDD
Libérer: 2024-12-12 12:22:24
original
296 Les gens l'ont consulté

Closures Unveiled: Exploring the Hidden Realms of JavaScript

Table des matières

  • Échapper au chaos du codage
  • Qu'est-ce qu'une fermeture exactement ?
  • Rupture : les fermetures dévoilées
  • Spellcraft pratique : un voyage de mise en cache avec des fermetures
  • Pièges courants et comment les éviter
  • Le voyage continue

Échapper au chaos du codage ?‍♂️

Avez-vous déjà eu l'impression que votre code avait son propre esprit : il devenait désordonné et refusait de rester organisé ? Ne vous inquiétez pas, nous sommes tous passés par là. JavaScript peut être délicat, même pour les assistants chevronnés. Et si je vous disais qu’il existe une arme secrète pour garder les choses sous contrôle ? Entrez les fermetures.

Considérez une fermeture comme un sac à dos magique que votre fonction transporte, stockant les variables et les mémoires dont elle pourrait avoir besoin plus tard. Ces petits éléments de magie de programmation permettent d'organiser votre code, de gérer l'état sans encombrement et d'ouvrir la porte à des modèles dynamiques et flexibles.

En maîtrisant les fermetures, vous débloquerez un nouveau niveau de puissance et d’élégance dans votre code. Alors, prenez votre baguette de codage (ou un café fort ☕) et aventurons-nous ensemble dans ces royaumes cachés. ?✨


Qu’est-ce qu’une fermeture exactement ? ?

Une fermeture est simplement une fonction qui mémorise les variables de son environnement d'origine, même après que cet environnement a cessé d'exister. Au lieu de supprimer ces variables, JavaScript les range, prêtes à être invoquées en cas de besoin.

const createCounter = () => {
    let count = 0; // Private variable in the closure's secret realm

    return () => {
        count++; // Whispers an increment to the hidden counter
        return count; // Reveal the mystical number
    };
}

// Summoning our magical counter
const counter = createCounter();

console.log(counter()); // Outputs: 1
console.log(counter()); // Outputs: 2
console.log(counter()); // Outputs: 3
console.log(counter.count);  // Outputs: undefined (`count` is hidden!) ?️‍♀️
Copier après la connexion
Copier après la connexion
Copier après la connexion

La fonction interne conserve l'accès à count, même si createCounter a fini de s'exécuter. Cette « mémoire » est l'essence même d'une fermeture : elle assure la sécurité de vos données et permet un code puissant et flexible. ?✨


En panne : les fermetures dévoilées ?

Bien que les fermetures puissent sembler magiques, elles sont simplement le résultat de la façon dont JavaScript gère la portée et la mémoire. Chaque fonction porte un lien vers son environnement lexical—le contexte dans lequel elle a été définie.

? Un environnement lexical est un enregistrement structuré de liaisons de variables, définissant ce qui est accessible dans cette portée. C'est comme une carte montrant quelles variables et fonctions vivent à l'intérieur d'un bloc ou d'une fonction donnée.

Les fermetures sont-elles des conteurs dynamiques ?

Les fermetures ne verrouillent pas une seule valeur ; ils suivent les changements au fil du temps. Si la variable de la portée externe est mise à jour, la fermeture voit la nouvelle valeur.

const createCounter = () => {
    let count = 0; // Private variable in the closure's secret realm

    return () => {
        count++; // Whispers an increment to the hidden counter
        return count; // Reveal the mystical number
    };
}

// Summoning our magical counter
const counter = createCounter();

console.log(counter()); // Outputs: 1
console.log(counter()); // Outputs: 2
console.log(counter()); // Outputs: 3
console.log(counter.count);  // Outputs: undefined (`count` is hidden!) ?️‍♀️
Copier après la connexion
Copier après la connexion
Copier après la connexion

Pourquoi les fermetures sont-elles des essentiels magiques ? ?

Les fermetures permettent l'encapsulation en créant des variables privées avec un accès contrôlé, gèrent l'état sur plusieurs appels sans compter sur des variables globales et alimentent des comportements dynamiques comme les usines, les rappels , et des crochets.

Les frameworks comme React exploitent ces pouvoirs, permettant aux composants fonctionnels de rester sans état tout en gérant l'état avec des hooks comme useState, tout cela grâce à la magie des fermetures.


Spellcraft pratique : un voyage de mise en cache avec des fermetures ?‍♂️

Les fermetures peuvent stocker l'état, ce qui les rend idéales pour des sorts tels que la mise en cache des résultats d'opérations coûteuses. Explorons cela étape par étape et améliorons notre sort au fur et à mesure.

Étape 1 : ?️ The Memory Keeper – Mise en cache de base

Notre premier sort est simple mais puissant : un gardien de mémoire. Si on lui demande à nouveau les mêmes entrées, il renvoie instantanément le résultat mis en cache.

// A variable in the global magical realm
let multiplier = 2;

const createMultiplier = () => {
  // The inner function 'captures' the essence of the outer realm
  return (value: number): number => value * multiplier;
}; 


// Our magical transformation function
const double = createMultiplier();

console.log(double(5)); // Outputs: 10
multiplier = 3;
console.log(double(5)); // Outputs: 15 (The magic adapts!) ✨
Copier après la connexion
Copier après la connexion

Étape 2 : ⏳ The Fading Spell – Cache expirant

Certains sorts, cependant, sont trop puissants pour durer éternellement. Améliorons notre cache avec la possibilité d'oublier les vieux souvenirs. Nous allons créer un CacheEntry pour stocker non seulement les valeurs, mais leur durée de vie magique.

(Remarquez comment nous nous appuyons sur l'idée précédente : les fermetures permettent d'ajouter facilement de la complexité sans perdre le fil.)

function withCache(fn: (...args: any[]) => any) {
  const cache: Record<string, any> = {};

  return (...args: any[]) => {
    const key = JSON.stringify(args);

    // Have we encountered these arguments before?
    if (key in cache) return cache[key]; // Recall of past magic! ?

    // First encounter? Let's forge a new memory
    const result = fn(...args);
    cache[key] = result;

    return result;
  };
}

// Example usage
const expensiveCalculation = (x: number, y: number) => {
  console.log('Performing complex calculation');
  return x * y;
};

// Summoning our magical cached calculation
const cachedCalculation = withCache(expensiveCalculation);

console.log(cachedCalculation(4, 5)); // Calculates and stores the spell
console.log(cachedCalculation(4, 5)); // Uses cached spell instantly
Copier après la connexion
Copier après la connexion

Étape 3 : ? Async Magic – Gestion des promesses

Parfois, les sorts nécessitent du temps, comme attendre qu'un oracle (ou une API) distant réponde. Notre sort peut également gérer cela. Il attendra la promesse, stockera la valeur résolue et la renverra dans le futur – pas de récupérations répétées.

type CacheEntry<T> = { value: T; expiry: number; };

function withCache<T extends (...args: any[]) => any>(
  fn: T,
  expirationMs: number = 5 * 60 * 1000, // Default 5 minutes
) {
  const cache = new Map<string, CacheEntry<ReturnType<T>>>();

  return (...args: Parameters<T>): ReturnType<T> => {
    const key = JSON.stringify(args);
    const now = Date.now(); // Current magical moment
    const cached = cache.get(key);

    // Is our magical memory still vibrant?
    if (cached && now < cached.expiry) return cached.value;

    // The memory has faded; it’s time to create new ones!
    const result = fn(...args);
    cache.set(key, { value: result, expiry: now + expirationMs });

    return result;
  };
}

// ...

const timeLimitedCalc = 
  withCache(expensiveCalculation, 3000); // 3-second cache

console.log(timeLimitedCalc(4, 5)); // Stores result with expiration
console.log(timeLimitedCalc(4, 5)); // Returns cached value before expiry

setTimeout(() => {
  console.log(timeLimitedCalc(4, 5)); // Recalculates after expiration
}, 3000);
Copier après la connexion
Copier après la connexion

Explorez le sort complet ici.

Le défi du sorcier ??‍♂️

Notre sort de mise en cache est puissant, mais ce n'est que le début. Pensez-vous pouvoir améliorer le code ? Pensez à ajouter une gestion des erreurs, à mettre en œuvre un nettoyage magique de la mémoire ou à créer des stratégies de mise en cache plus sophistiquées. Le véritable art du codage réside dans l’expérimentation, en repoussant les limites et en réinventant les possibilités ! ??


Pièges courants et comment les éviter ?️

Les fermetures sont puissantes, mais même les meilleures périodes comportent des risques. Découvrons quelques pièges courants et leurs solutions pour vous aider à gérer les fermetures en toute confiance.

Piège n°1 : ?️ Le piège à boucle sournoise

Un piège JavaScript classique, souvent présenté dans les entretiens de codage, implique les boucles, en particulier la façon dont elles gèrent les variables et les fermetures de boucle.

// ...

// The memory has faded; it’s time to create new ones!
const result = fn(...args);

if (result instanceof Promise) {
  return result.then((value) => {
    cache.set(key, { value, expiry: now + expirationMs });
    return value;
  });
}

// ...
Copier après la connexion
Copier après la connexion

L'exemple ci-dessus enregistre le nombre 5 cinq fois car var crée une variable unique partagée pour toutes les fermetures.

Solution 1 : utilisez let pour garantir la portée du bloc.
Le mot-clé let crée une nouvelle variable de portée bloc pour chaque itération, afin que les fermetures capturent la valeur correcte.

const createCounter = () => {
    let count = 0; // Private variable in the closure's secret realm

    return () => {
        count++; // Whispers an increment to the hidden counter
        return count; // Reveal the mystical number
    };
}

// Summoning our magical counter
const counter = createCounter();

console.log(counter()); // Outputs: 1
console.log(counter()); // Outputs: 2
console.log(counter()); // Outputs: 3
console.log(counter.count);  // Outputs: undefined (`count` is hidden!) ?️‍♀️
Copier après la connexion
Copier après la connexion
Copier après la connexion

Solution 2 : utilisez une IIFE (expression de fonction immédiatement invoquée).
Un IIFE crée une nouvelle portée pour chaque itération, garantissant une gestion appropriée des variables dans la boucle.

// A variable in the global magical realm
let multiplier = 2;

const createMultiplier = () => {
  // The inner function 'captures' the essence of the outer realm
  return (value: number): number => value * multiplier;
}; 


// Our magical transformation function
const double = createMultiplier();

console.log(double(5)); // Outputs: 10
multiplier = 3;
console.log(double(5)); // Outputs: 15 (The magic adapts!) ✨
Copier après la connexion
Copier après la connexion

Astuce bonus : ? L'astuce fonctionnelle.
Peu de sorciers connaissent ce sort, et pour être honnête, je l’ai rarement (voire jamais) vu mentionné lors des entretiens de codage. Saviez-vous que setTimeout peut transmettre des arguments supplémentaires directement à son rappel ?

function withCache(fn: (...args: any[]) => any) {
  const cache: Record<string, any> = {};

  return (...args: any[]) => {
    const key = JSON.stringify(args);

    // Have we encountered these arguments before?
    if (key in cache) return cache[key]; // Recall of past magic! ?

    // First encounter? Let's forge a new memory
    const result = fn(...args);
    cache[key] = result;

    return result;
  };
}

// Example usage
const expensiveCalculation = (x: number, y: number) => {
  console.log('Performing complex calculation');
  return x * y;
};

// Summoning our magical cached calculation
const cachedCalculation = withCache(expensiveCalculation);

console.log(cachedCalculation(4, 5)); // Calculates and stores the spell
console.log(cachedCalculation(4, 5)); // Uses cached spell instantly
Copier après la connexion
Copier après la connexion

Piège n°2 : ? Fuites de mémoire – Menace silencieuse

Les fermetures conservent une référence à leur portée externe, ce qui signifie que les variables peuvent rester plus longtemps que prévu, entraînant des fuites de mémoire.

type CacheEntry<T> = { value: T; expiry: number; };

function withCache<T extends (...args: any[]) => any>(
  fn: T,
  expirationMs: number = 5 * 60 * 1000, // Default 5 minutes
) {
  const cache = new Map<string, CacheEntry<ReturnType<T>>>();

  return (...args: Parameters<T>): ReturnType<T> => {
    const key = JSON.stringify(args);
    const now = Date.now(); // Current magical moment
    const cached = cache.get(key);

    // Is our magical memory still vibrant?
    if (cached && now < cached.expiry) return cached.value;

    // The memory has faded; it’s time to create new ones!
    const result = fn(...args);
    cache.set(key, { value: result, expiry: now + expirationMs });

    return result;
  };
}

// ...

const timeLimitedCalc = 
  withCache(expensiveCalculation, 3000); // 3-second cache

console.log(timeLimitedCalc(4, 5)); // Stores result with expiration
console.log(timeLimitedCalc(4, 5)); // Returns cached value before expiry

setTimeout(() => {
  console.log(timeLimitedCalc(4, 5)); // Recalculates after expiration
}, 3000);
Copier après la connexion
Copier après la connexion

Que se passe-t-il ici ? La fermeture conserve l'intégralité de la variable de données, même si seule une petite partie est nécessaire, ce qui risque de gaspiller des ressources importantes.

La solution consiste à gérer soigneusement ce que les fermetures capturent et à libérer explicitement les références inutiles. Cela garantit que les grands ensembles de données sont chargés uniquement en cas de besoin et libérés de manière proactive grâce à une méthode de nettoyage.

// ...

// The memory has faded; it’s time to create new ones!
const result = fn(...args);

if (result instanceof Promise) {
  return result.then((value) => {
    cache.set(key, { value, expiry: now + expirationMs });
    return value;
  });
}

// ...
Copier après la connexion
Copier après la connexion

Piège n°3 : ?️ Le chaos des mutations

Les fermetures peuvent entraîner un comportement inattendu lorsque l'état partagé est muté. Ce qui semble être une simple référence peut entraîner des effets secondaires involontaires.

for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i); // Logs 5, five times ?
  }, i * 1000); 
}
Copier après la connexion

Que se passe-t-il ici ? La méthode getUsers expose le tableau des utilisateurs, rompant l'encapsulation et risquant des effets secondaires involontaires dus à des modifications externes.

La solution est de renvoyer une copie de l'état interne. Cela empêche les modifications externes, maintient l'intégrité des données et sauvegarde la logique interne de la fermeture.

for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i); // Works as expected ?
  }, i * 1000);
}
Copier après la connexion

Les règles d’or de la maîtrise de la clôture ?

  1. Soyez intentionnel à propos des captures : comprenez ce que les fermetures capturent pour éviter les dépendances inutiles et les problèmes de mémoire.
  2. Portée judicieuse des variables : utilisez la portée des blocs pour éviter les bogues de référence partagée et garantir une capture correcte des variables.
  3. Adoptez l'immuabilité : privilégiez les modèles immuables, en renvoyant des copies au lieu de modifier l'état partagé, pour éviter les effets secondaires.
  4. Pratiquez le nettoyage : libérez les références inutiles pour éviter les fuites de mémoire, en particulier avec des données volumineuses ou sensibles.

La maîtrise de ces techniques vous aidera à exercer la magie des fermetures en toute confiance. La véritable maîtrise réside dans la compréhension et non dans l’évitement. ✨


Le voyage continue

Les fermetures peuvent sembler complexes au premier abord, mais elles libèrent le potentiel d'écrire du code plus élégant et plus efficace. En transformant des fonctions simples en entités persistantes et avec état, les fermetures peuvent partager avec élégance des secrets à travers le temps et l'espace. Cette fonctionnalité puissante élève JavaScript du statut de simple langage de script à celui d'un outil puissant et flexible pour résoudre des problèmes complexes.

Votre voyage ne s’arrête pas là ; approfondissez les modèles asynchrones, la programmation fonctionnelle et le fonctionnement interne des moteurs JavaScript. Chaque étape révèle davantage de couches de ce langage enchanteur, suscitant de nouvelles idées et solutions.

Après tout, la véritable maîtrise vient de la curiosité et de l’exploration. Que votre code soit toujours élégant, efficace et un peu magique. ?

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal