クロージャが状態を取り込む仕組み
クロージャのレキシカル環境を理解します。
「クロージャが状態を取り込む仕組み」はCoddyKit上の無料JavaScript Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJavaScript Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 JavaScript Academyコースには全4レッスンが含まれています。
クロージャとは
クロージャとは、定義されたスコープにある変数への参照と関数をひとまとめにしたものです。内側の関数は、外側の関数が戻った後も、それらの変数を「覚えています」。
function outer() {
const message = "hello";
function inner() {
console.log(message); // sees outer variable
}
inner();
}
outer(); // "hello"内側の関数を返す
内側の関数を返すと、クロージャの仕組みが現れます。outerの実行がすでに終了していても、外側の変数へのアクセスを維持します。
function outer() {
const message = "remembered";
return function () {
console.log(message);
};
}
const fn = outer();
fn(); // "remembered"レキシカルスコープ
クロージャが機能するのは、レキシカルスコープ(静的スコープ)の仕組みによるものです。関数は、呼び出された場所ではなく、ソースコード上で物理的に囲んでいるスコープに定義された変数へアクセスできます。
const x = 10;
function show() {
console.log(x); // resolves where defined
}
function run() {
const x = 99;
show(); // still logs 10, not 99
}
run();参照によるキャプチャ
クロージャがキャプチャするのは値のスナップショットではなく、変数そのものです。そのため、後から変数が変更されると、クロージャからは新しい値が見えます。
function make() {
let count = 0;
const get = () => count;
count = 5; // changed after get defined
return get;
}
console.log(make()()); // 5呼び出しごとに新しいスコープが作られる
外側の関数を呼び出すたびに、新しい変数の集合が作成されます。異なる呼び出しから作られたクロージャ同士が状態を共有することはありません。
function counter() {
let n = 0;
return () => ++n;
}
const a = counter();
const b = counter();
console.log(a(), a()); // 1 2
console.log(b()); // 1 (independent)パラメーターを取り込むクロージャ
関数のパラメーターも取り込まれます。これにより、汎用的な関数から特化した関数を作成できます。
function adder(x) {
return (y) => x + y;
}
const add5 = adder(5);
console.log(add5(3)); // 8
console.log(add5(10)); // 15古典的なループの罠
有名なバグがあります。ループ内でvarを使うと、すべてのクロージャで1つの変数が共有されます。クロージャが実行される頃にはループが終わっているため、すべてが最後の値を参照します。
const fns = [];
for (var i = 0; i < 3; i++) {
fns.push(() => i);
}
console.log(fns[0](), fns[1](), fns[2]()); // 3 3 3let による解決
letは反復ごとに新しいバインディングを作成するため、各クロージャが独自のiを取り込みます。これが現在の一般的な解決方法です。
const fns = [];
for (let i = 0; i < 3; i++) {
fns.push(() => i);
}
console.log(fns[0](), fns[1](), fns[2]()); // 0 1 2メモリを保持するクロージャ
クロージャが存在する限り、クロージャが取り込んだ変数はガベージコレクションの対象になりません。通常は問題ありませんが、必要以上に長くクロージャを保持するとメモリリークの原因になることがあります。
function make() {
const big = new Array(3).fill("data");
return () => big.length;
}
const f = make();
console.log(f()); // 3 - big stays in memory via fクロージャはあらゆる場所で使われる
コールバック、イベントハンドラー、setTimeout、配列メソッド、Promiseはすべて、コンテキストを記憶するためにクロージャを利用しています。多くの場合、意識することなく常に使っています。
function greet(name) {
return () => console.log("Hi " + name);
}
[greet("Ada"), greet("Bob")].forEach((fn) => fn());
// Hi Ada
// Hi Bob取り込まれた状態を調べる
同じ呼び出しから作られた複数のクロージャは、同じ取り込まれた変数を共有します。そのため、一方が書き込んだ値をもう一方が読み取れます。
function pair() {
let value = 0;
return {
set: (v) => { value = v; },
get: () => value
};
}
const p = pair();
p.set(42);
console.log(p.get()); // 42クイックチェック
クロージャが状態を取り込む仕組み
まとめ
クロージャとは、関数と、その関数を定義したスコープへの参照を組み合わせたものです。レキシカルスコープにより、外側の関数が戻った後も外側の変数を記憶し、それらを値ではなく参照によって取り込みます。外側の関数を呼び出すたびに独立したスコープが作られます。ループの罠を避けるにはletを使います。クロージャは取り込んだメモリを保持し、あらゆる場所でコールバックを支えています。
よくある質問
「クロージャが状態を取り込む仕組み」レッスンは無料ですか?
はい。「クロージャが状態を取り込む仕組み」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、JavaScript Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 JavaScript Academyコースには全4レッスンが含まれています。
「クロージャが状態を取り込む仕組み」で何を学びますか?
クロージャのレキシカル環境を理解します。 ブラウザで直接実行するハンズオンコードでJavaScript Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
JavaScript Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJavaScript Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「クロージャが状態を取り込む仕組み」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJavaScript Academyレッスンでコードを書いて実行できますか?
はい。すべてのJavaScript Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- クロージャが状態を取り込む仕組み
- クロージャによるプライベート変数
- モジュールパターン(IIFE)
- ファクトリ関数