ホームページ > ウェブフロントエンド > jsチュートリアル > クロージャの公開: JavaScript の隠された領域を探索する

クロージャの公開: JavaScript の隠された領域を探索する

DDD
リリース: 2024-12-12 12:22:24
オリジナル
286 人が閲覧しました

Closures Unveiled: Exploring the Hidden Realms of JavaScript

目次

  • コーディングの混乱からの脱出
  • クロージャとは正確には何ですか?
  • 内訳: 閉鎖が明らかに
  • 実践的な呪文: クロージャを使用したキャッシュの旅
  • よくある落とし穴とその回避方法
  • 旅は続く

コーディングの混乱から逃れる ?‍♂️

コードが独自の考えを持ち、乱雑になり、整理された状態を維持できなくなっていると感じたことはありませんか?心配しないでください、私たちは皆そこにいたことがあります。 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 のようなフックで状態を管理しながら機能コンポーネントをステートレスに保ちます。これはすべてクロージャーの魔法のおかげです。


実践的な呪文: クロージャを使用したキャッシュの旅 ?‍♂️

クロージャは状態を保存できるため、負荷の高い操作の結果をキャッシュするような呪文に最適です。これを段階的に調べて、呪文を強化していきましょう。

ステップ 1: ?️ メモリーキーパー – 基本的なキャッシュ

最初の呪文はシンプルですが強力です。それは記憶の保持者です。同じ入力を再度要求されると、キャッシュされた結果が即座に返されます。

// 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!) ✨
ログイン後にコピー
ログイン後にコピー

ステップ 2: ⏳ 消えゆく呪文 – キャッシュの期限切れ

しかし、一部の呪文は強力すぎて永久に持続できません。古い記憶を忘れる能力を備えたキャッシュを強化しましょう。値だけでなく、その魔法の寿命も保存する 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
ログイン後にコピー
ログイン後にコピー

ステップ 3: ?非同期マジック – Promise 処理

遠くのオラクル (または 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);
ログイン後にコピー
ログイン後にコピー

ここで呪文の全文をご覧ください。

魔法使いの挑戦??‍♂️

私たちのキャッシュ呪文は強力ですが、それはほんの始まりにすぎません。コードをレベルアップできると思いますか?エラー処理の追加、魔法のようなメモリ クリーンアップの実装、またはより洗練されたキャッシュ戦略の作成を検討してください。コーディングの真の芸術は、実験し、限界を押し広げ、可能性を再考することにあります。 ??


よくある落とし穴とその回避方法 ?️

クロージャは強力ですが、最良の呪文にもリスクが伴います。自信を持ってクロージャを使用できるように、いくつかの一般的な落とし穴とその解決策を明らかにしましょう。

落とし穴 #1: ?️ 卑劣なループの罠

コーディングのインタビューでよく取り上げられる古典的な 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
ログイン後にコピー
ログイン後にコピー

落とし穴 #2: ?メモリリーク – サイレント脅威

クロージャは外側のスコープへの参照を維持します。これは、変数が予想よりも長く留まり、メモリ リークが発生する可能性があることを意味します。

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;
  });
}

// ...
ログイン後にコピー
ログイン後にコピー

落とし穴 #3: ?️ 突然変異による騒乱

共有状態が変化すると、クロージャにより予期しない動作が発生する可能性があります。単純な参照のように見えるものでも、予期しない副作用が生じる可能性があります。

for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i); // Logs 5, five times ?
  }, i * 1000); 
}
ログイン後にコピー

ここで何が起こっているのでしょうか? getUsers メソッドは us​​ers 配列を公開するため、カプセル化が破壊され、外部変更による意図しない副作用が生じる危険があります。

解決策は、内部状態のコピーを返すことです。これにより、外部からの変更が防止され、データの整合性が維持され、クロージャの内部ロジックが保護されます。

for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i); // Works as expected ?
  }, i * 1000);
}
ログイン後にコピー

クロージャをマスターするための黄金律 ?

  1. キャプチャについては意識的に行う: 不必要な依存関係やメモリの問題を避けるために、クロージャがどのようなキャプチャをキャプチャするのかを理解してください。
  2. 変数の有効範囲を賢く設定する: ブロックの有効範囲を使用して、共有参照のバグを防ぎ、変数を正しくキャプチャします。
  3. 不変性を受け入れる: 副作用を避けるために、共有状態を変更するのではなくコピーを返す不変パターンを優先します。
  4. クリーンアップの実践: 特に大規模なデータや機密データの場合、メモリ リークを防ぐために不要な参照を解放します。

これらのテクニックをマスターすると、クロージャの魔法を自信を持って行使できるようになります。真の熟練とは、回避することではなく、理解することにあります。 ✨


旅は続く

クロージャーは最初は複雑に見えるかもしれませんが、よりエレガントで効率的なコードを作成できる可能性が開かれます。単純な関数を永続的なステートフルなエンティティに変えることで、クロージャは時間と空間を越えてエレガントにシークレットを共有できます。この強力な機能により、JavaScript は単純なスクリプト言語から、複雑な問題を解決するための強力で柔軟なツールに昇格します。

あなたの旅はここで終わりません。非同期パターン、関数型プログラミング、JavaScript エンジンの内部動作についてさらに詳しく説明します。各ステップでは、この魅力的な言語のさらなる層が明らかになり、新しいアイデアやソリューションが生まれます。

結局のところ、真の熟練は好奇心と探求から生まれます。あなたのコードがエレガントで効率的、そして少し魔法のようになりますように。 ?

以上がクロージャの公開: JavaScript の隠された領域を探索するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート