クロージャを理解するためのポイント
JavaScriptは関数の中に関数を定義できる。
JavaScript特有の構文になります。(JavaやC言語などではできない。)
1 2 3 4 5 6 7 8 |
function parentFunc() { console.log('親です。'); function childrenFunc() { console.log('子供です。'); } } parentFunc(); |
JavaScriptの関数は変数の中に入れて変数として持ち運べる。
この性質もできない言語はいっぱいあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function parentFunc() { console.log('親です。'); function childrenFunc() { console.log('子供です。'); } return childrenFunc; } function reserveFunc(a) { a(); } const child = parentFunc(); // 親です。 child(); // 子供です。 reserveFunc(child); // 子供です。 |
JavaScriptの関数は親の変数の値を使える。
JavaScriptの関数は別の関数の変数を使うことができません。しかし、関数が親子関係にある場合は親の関数の変数を使えます。(なお、親だけでなく祖父母など祖先要素は全て参照できます。:スコープチェイン)
1 2 3 4 5 6 7 8 |
function parentFunc() { const oyaValue = '親'; console.log('親です。'); function childrenFunc() { console.log(`${oyaValue}の子供です。`); } return childrenFunc; } |
JavaScriptの関数の「親」は「生みの親」のこと。
実行されたタイミングではなく、定義された場所の親を親の関数とします。これを「レキシカルスコープ」と呼びます。(なお、実行されたタイミングの関数を親とする考え方を「ダイナミックスコープ」と呼びます。JavaScriptはダイナミックスコープではないですが。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function parentFunc() { const oyaValue = '親'; console.log('親です。'); function childrenFunc() { console.log(`${oyaValue}の子供です。`); } return childrenFunc; } function reserveFunc(a) { const betujin = '別人'; a(); // betujinという変数にはアクセスできない。 } const child = parentFunc(); // 親です。 child(); // 子供です。 reserveFunc(child); // 子供です。 |
「クロージャ」とは?
一言で言えば、「親のローカル変数を参照している関数内関数のこと」で、一時記憶領域を提供し続けることができます。
通常は、ローカル変数は、関数が呼ばれ終わったら内容がクリアされます。
しかし、関数内の関数がローカル変数を参照している場合は、関数終了後も、ローカル変数の値は保持され続けます。
用途
管理しやすい変数を作るため
グローバル変数だと値がちゃんと正常か常に気を使わないとダメですが、クロージャーの変数なら気を使う必要がないです。(外側からは絶対にアクセスがされないプライベート変数になる。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
let generatePerson = () => { let age = 0; return { getAge: () => age, incrementAge: () => { age += 1; console.log("age", age); }, }; }; const person = generatePerson(); person.incrementAge(); person.incrementAge(); console.log(person.getAge()); |
generatePersonのincrementAgeでそのままオブジェクトを返してしまうと外側から操作できてしまいますが、関数を返せば外側からは自由に値を変更できません。
getAgeもそのままageを返すのではなく関数を返しているのが特徴です。
コールバック関数をカスタマイズしてコードをスッキリさせれる。
コールバック関数によっては決められたシグネチャしか受け付けてくれない。しかし、クロージャを使って関数を再定義してあげることによって別途条件分岐して定義することができる。
利点としては、コールバック地獄を脱出させてコードをスッキリ記述するための手段として使われる。(今までは入れ子にしまくっていたのがクロージャを使えば引数として必要なものを渡してあげれば、親の変数を使うことができるので外だしすることができるため。)
ただ、今はPromiseやasync、awaitなどの構文があるためそちらでも代替は可能。
例
定義
以下の例で言えば、「innerFunc」がクロージャになります。(通常は無名関数にすることが多いです。)
1 2 3 4 5 6 7 8 9 10 |
function outerFunc() { const a = 1; function innerFunc() { a = a + 1 } return innerFunc(); } |
呼び出し
1 2 3 4 5 |
var closure = outerFunc() closure(); // 1 closure(); // 2 closure(); // 3 |
この記事へのコメントはありません。