JavaScript でのループ クロージャの解明
JavaScript 開発者は、ループ内でクロージャを使用すると、予期しない動作に遭遇することがよくあります。この問題は、特にクロージャの概念に慣れていない人にとっては混乱を招く可能性があります。
この記事では、一般的な落とし穴を説明する実践的な例を検討し、イベント リスナー、非同期コード、配列の反復処理など、ループ内でクロージャを効果的に使用するための解決策を紹介します。
指示 | 説明 |
---|---|
let | ブロックスコープのローカル変数を宣言し、オプションで値に初期化します。ループの各反復に独自のスコープがあることを確認するために使用されます。 |
const | ブロックスコープの読み取り専用の名前付き定数を宣言します。値が変更されない関数または変数を作成するために使用されます。 |
Promise | 非同期操作の最終的な完了 (または失敗) とその結果の値を表します。 |
setTimeout | 指定されたミリ秒数後に関数を呼び出すか、式を評価します。 |
addEventListener | 既存のイベント ハンドラーを上書きせずに、指定された要素にイベント ハンドラーをアタッチします。 |
IIFE | すぐに呼び出される関数式。定義されるとすぐに実行される関数。ループ内でローカル スコープを作成するために使用されます。 |
for...in | オブジェクトの列挙可能なプロパティを任意の順序で反復処理します。 |
for...of | 反復可能なオブジェクト (配列や文字列など) の値を特定の順序で反復します。 |
ループ内の JavaScript クロージャを理解する
前の例で提供されたスクリプトは、JavaScript のループ内のクロージャに関する一般的な問題に対処します。を使用するときは、 var ループ内の宣言では、すべての反復が同じ関数スコープを共有します。最初の例では、出力が 3 回「My value: 3」となっているのはこのためです。解決策は使用することです let、各反復で正しい値を維持するブロック スコープを作成します。このアプローチにより、各反復に独自のスコープが設定されるため、関数が呼び出されたときに正しい値が保持されます。このスクリプトは、宣言を次のように変更する方法を示しています。 var に let 問題が修正され、意図したとおりに「私の値: 0」、「私の値: 1」、「私の値: 2」が記録されます。
非同期コードの場合も、同じクロージャの問題が発生する可能性があります。使用する Promises そして setTimeout を備えた関数 let 各非同期呼び出しが正しい反復値を維持することを保証します。スクリプトは、次を使用してそれを示します wait と let、解決された各 Promise は期待値を記録します。イベント リスナーも同様の問題に直面する可能性があります。ただし、リスナー関数を IIFE (即時に呼び出される関数式) は、反復ごとに新しいスコープを作成することで、正しい値を取得するのに役立ちます。の用法 for...in そして for...of ループはさらに、クロージャでのスコープ設定の重要性を示し、 を使用してインデックスと値を正しくキャプチャする方法を示します。 IIFE ループ反復ごとに個別のスコープを作成します。
let を使用した JavaScript ループのクロージャの問題の解決
JavaScript (ES6)
let funcs = [];
// Let's create 3 functions
for (let i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value:", i);
};
}
for (let j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
非同期コードでの正しいクロージャ値の確保
JavaScript (ES6)
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
for (let i = 0; i < 3; i++) {
// Log `i` as soon as each promise resolves.
wait(i * 100).then(() => console.log(i));
}
IIFE を使用したイベント リスナーの正しいクロージャ
JavaScript (ES6)
var buttons = document.getElementsByTagName("button");
// Let's create 3 functions
for (var i = 0; i < buttons.length; i++) {
// as event listeners
(function(i) {
buttons[i].addEventListener("click", function() {
// each should log its value.
console.log("My value:", i);
});
})(i);
}
for...in および for...of ループによる正しいクロージャ
JavaScript (ES6)
const arr = [1, 2, 3];
const fns = [];
for (const i in arr) {
fns.push(((i) => () => console.log("index:", i))(i));
}
for (const v of arr) {
fns.push(((v) => () => console.log("value:", v))(v));
}
for (const n of arr) {
const obj = { number: n };
fns.push(((n, obj) => () => console.log("n:", n, "|", "obj:", JSON.stringify(obj)))(n, obj));
}
for (const f of fns) {
f();
}
高度な JavaScript 関数でのクロージャの使用法を調べる
クロージャは JavaScript の基本的な概念であり、スコープが閉じられた後でも、関数がそのスコープを囲んでいるスコープから変数にアクセスできるようにします。この機能は、メモ化、カリー化、関数型プログラミングなどで使用される高度な関数を作成する場合に特に強力です。たとえば、メモ化ではクロージャを利用して負荷の高い関数呼び出しの結果を記憶し、同じ入力が再度発生したときにキャッシュされた結果を返します。クロージャを利用することで、特にフィボナッチ数列の計算などの再帰関数のパフォーマンスを向上させる、より効率的で最適化されたコードを作成できます。
クロージャのもう 1 つの高度な使用法は、JavaScript オブジェクト内にプライベート変数と関数を作成し、プライベート メソッドとプロパティをシミュレートすることです。この手法は、機能をカプセル化し、グローバル スコープの汚染を避けるために、モジュール パターンや即時呼び出し関数式 (IIFE) でよく使用されます。さらに、クロージャはイベント処理と非同期プログラミングにおいて重要な役割を果たし、長期にわたって状態とコンテキストを保持するのに役立ちます。クロージャを理解し効果的に利用すると、JavaScript プログラミング スキルが大幅に向上し、よりモジュール化された、再利用可能で保守しやすいコードを作成できるようになります。
JavaScript の閉鎖に関するよくある質問
- JavaScript のクロージャーとは何ですか?
- クロージャは、関数がそのスコープ外で実行された場合でも、その字句スコープへのアクセスを保持する関数です。
- ループ内でクロージャが発生するのはなぜですか?
- ループ内のクロージャは、ループが同じ変数参照をキャプチャする関数を作成するために発生し、正しく処理されないと予期しない動作を引き起こします。
- ループ内のクロージャの問題を解決するにはどうすればよいでしょうか?
- 使用する let の代わりに var ループ内または使用 IIFE (即時に呼び出される関数式) は、反復ごとに新しいスコープを作成することでクロージャの問題を解決できます。
- IIFEとは何ですか?
- アン IIFE は、作成直後に実行される関数であり、新しいスコープを作成し、変数の競合を回避するためによく使用されます。
- クロージャは非同期プログラミングで使用できますか?
- はい、クロージャは非同期プログラミングにおいて、Promise やコールバックなどの非同期操作全体で状態とコンテキストを維持するために不可欠です。
- メモ化とは何ですか?クロージャはどのように役立ちますか?
- メモ化は、高価な関数呼び出しの結果をキャッシュする最適化手法です。クロージャは、複数の関数呼び出しにわたってキャッシュへのアクセスを保持することで役立ちます。
- クロージャはイベント処理にどのように役立ちますか?
- クロージャは、イベント ハンドラーに必要な変数の状態を保持し、イベントがトリガーされたときに変数が正しく機能することを保証します。
- JavaScriptのモジュールパターンとは何ですか?
- モジュール パターンはクロージャを使用してプライベート変数と関数を作成し、機能をカプセル化し、グローバル スコープの汚染を回避します。
- クロージャは JavaScript のプライベート メソッドをシミュレートできますか?
- はい、クロージャは、変数と関数が定義されている関数のスコープ内でのみアクセスできるようにすることで、プライベート メソッドをシミュレートできます。
ループ内の JavaScript のクロージャに関する最終的な考え
JavaScript のクロージャ、特にループ内でのクロージャを習得することは、予測可能で効率的なコードを作成するために非常に重要です。活用することで let、 Promises、 そして IIFEを使用すると、開発者はよくある落とし穴を回避し、正しい変数スコープを確保できます。この理解により、非同期タスクやイベント駆動型プログラミングを処理する能力が強化され、最終的にはより堅牢なアプリケーションが実現します。