コードが独自の考えを持ち、乱雑になり、整理された状態を維持できなくなっていると感じたことはありませんか?心配しないでください、私たちは皆そこにいたことがあります。 JavaScript は、熟練したウィザードであっても難しい場合があります。しかし、物事を制御するための秘密兵器があると言ったらどうなるでしょうか? クロージャを入力してください。
クロージャーは、関数が運ぶ魔法のバックパックとして、後で必要になる可能性のある変数とメモリを保存すると考えてください。これらの小さなプログラミングの魔法は、コードを整理し、乱雑にならずに状態を管理し、動的で柔軟なパターンへの扉を開きます。
クロージャーをマスターすることで、コードのパワーと優雅さを新たなレベルに引き上げることができます。それでは、コーディングの杖 (または濃いコーヒー ☕) を手に取り、これらの隠された領域に一緒に冒険してみましょう。 ?✨
クロージャは、元の環境が存在しなくなった後でも、元の環境の変数を記憶する単純な関数です。 JavaScript はこれらの変数を破棄する代わりに、必要なときに呼び出せるように、変数を隠しておきます。
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!) ?️♀️
createCounter の実行が終了しても、内部関数は count へのアクセスを保持します。この「メモリ」はクロージャの本質であり、データを安全に保ち、強力で柔軟なコードを可能にします。 ?✨
クロージャは魔法のように思えるかもしれませんが、JavaScript が スコープ と メモリ を処理する方法の結果にすぎません。すべての関数には、その語彙環境、つまり関数が定義されたコンテキストへのリンクが含まれています。
? 字句環境は変数バインディングの構造化された記録であり、そのスコープ内でアクセスできるものを定義します。これは、特定のブロックまたは関数内にどの変数や関数が存在するかを示すマップのようなものです。
クロージャは 1 つの値だけをロックするわけではありません。時間の経過に伴う変化を追跡します。外側のスコープの変数が更新されると、クロージャは新しい値を参照します。
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!) ?️♀️
クロージャは、アクセスが制御されたプライベート変数を作成することでカプセル化を可能にし、グローバルに依存せずに複数の呼び出しにわたって状態を管理し、ファクトリやコールバックなどの動的動作を強化します。 、フック。
React のようなフレームワークはこれらの機能を活用し、useState のようなフックで状態を管理しながら機能コンポーネントをステートレスに保ちます。これはすべてクロージャーの魔法のおかげです。
クロージャは状態を保存できるため、負荷の高い操作の結果をキャッシュするような呪文に最適です。これを段階的に調べて、呪文を強化していきましょう。
最初の呪文はシンプルですが強力です。それは記憶の保持者です。同じ入力を再度要求されると、キャッシュされた結果が即座に返されます。
// 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!) ✨
しかし、一部の呪文は強力すぎて永久に持続できません。古い記憶を忘れる能力を備えたキャッシュを強化しましょう。値だけでなく、その魔法の寿命も保存する CacheEntry を作成します。
(前のアイデアに基づいて構築していることに注目してください。クロージャを使用すると、道を見失うことなく複雑さを簡単に追加できます。)
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
遠くのオラクル (または API) が応答するのを待つなど、呪文には時間がかかる場合があります。私たちの呪文はそれにも対応できます。 Promise を待ち、解決された値を保存し、将来それを返します。フェッチを繰り返すことはありません。
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);
ここで呪文の全文をご覧ください。
私たちのキャッシュ呪文は強力ですが、それはほんの始まりにすぎません。コードをレベルアップできると思いますか?エラー処理の追加、魔法のようなメモリ クリーンアップの実装、またはより洗練されたキャッシュ戦略の作成を検討してください。コーディングの真の芸術は、実験し、限界を押し広げ、可能性を再考することにあります。 ??
クロージャは強力ですが、最良の呪文にもリスクが伴います。自信を持ってクロージャを使用できるように、いくつかの一般的な落とし穴とその解決策を明らかにしましょう。
コーディングのインタビューでよく取り上げられる古典的な JavaScript の落とし穴には、ループ、特にループ変数とクロージャの処理方法が関係します。
// ... // 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; }); } // ...
上の例では、var がすべてのクロージャに対して単一の共有変数を作成するため、数値 5 を 5 回記録します。
解決策 1: let を使用してブロックのスコープを確保します。
let キーワードは反復ごとに新しいブロック スコープの変数を作成するため、クロージャは正しい値を取得します。
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!) ?️♀️
解決策 2: IIFE (即時に呼び出される関数式) を使用します。
IIFE は反復ごとに新しいスコープを作成し、ループ内で適切な変数処理を保証します。
// 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!) ✨
ボーナスヒント: ?機能的なトリック。
この呪文を知っている魔法使いはほとんどいませんし、正直に言うと、コーディングの面接でこの呪文について言及されているのを(あったとしても)ほとんど見たことがありません。 setTimeout は追加の引数をコールバックに直接渡すことができることをご存知ですか?
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
クロージャは外側のスコープへの参照を維持します。これは、変数が予想よりも長く留まり、メモリ リークが発生する可能性があることを意味します。
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);
ここで何が起こっているのでしょうか?クロージャは、必要なのはほんの一部であっても、データ変数全体を保持し、大量のリソースを浪費する可能性があります。
解決策は、どのクロージャが不必要な参照をキャプチャし、明示的に解放するかを慎重に管理することです。これにより、大規模なデータセットが必要な場合にのみロードされ、クリーンアップ メソッドを使用して積極的に解放されることが保証されます。
// ... // 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; }); } // ...
共有状態が変化すると、クロージャにより予期しない動作が発生する可能性があります。単純な参照のように見えるものでも、予期しない副作用が生じる可能性があります。
for (var i = 0; i < 5; i++) { setTimeout(() => { console.log(i); // Logs 5, five times ? }, i * 1000); }
ここで何が起こっているのでしょうか? getUsers メソッドは users 配列を公開するため、カプセル化が破壊され、外部変更による意図しない副作用が生じる危険があります。
解決策は、内部状態のコピーを返すことです。これにより、外部からの変更が防止され、データの整合性が維持され、クロージャの内部ロジックが保護されます。
for (let i = 0; i < 5; i++) { setTimeout(() => { console.log(i); // Works as expected ? }, i * 1000); }
これらのテクニックをマスターすると、クロージャの魔法を自信を持って行使できるようになります。真の熟練とは、回避することではなく、理解することにあります。 ✨
クロージャーは最初は複雑に見えるかもしれませんが、よりエレガントで効率的なコードを作成できる可能性が開かれます。単純な関数を永続的なステートフルなエンティティに変えることで、クロージャは時間と空間を越えてエレガントにシークレットを共有できます。この強力な機能により、JavaScript は単純なスクリプト言語から、複雑な問題を解決するための強力で柔軟なツールに昇格します。
あなたの旅はここで終わりません。非同期パターン、関数型プログラミング、JavaScript エンジンの内部動作についてさらに詳しく説明します。各ステップでは、この魅力的な言語のさらなる層が明らかになり、新しいアイデアやソリューションが生まれます。
結局のところ、真の熟練は好奇心と探求から生まれます。あなたのコードがエレガントで効率的、そして少し魔法のようになりますように。 ?
以上がクロージャの公開: JavaScript の隠された領域を探索するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。