2021年12月18日 JavaScript (JS Primer) 関数とスコープ
クロージャー
- クロージャーとは「外側のスコープにある変数への参照を保持できる」という関数が持つ性質を指します。
- ほぼ全ての関数はクロージャーとも言えます。厳密にいうと、特定の変数を参照することで関数が状態を持っていることをそう呼びます。
ガベージコレクション
JavaScriptではガベージコレクションが採用されています。 ガベージコレクションとは、どこからも参照されなくなったデータを不要なデータと判断して自動的にメモリ上から解放する仕組みのことです。
- 関数の返り値が変数をひとつでも参照している場合はガベージコレクションによりメモリが開放されることはありません。
ここまで読んで何かに例えた方が記憶に定着できるのかと感じたのでポケモンに例えてみました。 クロージャーはモンスターボールに例えると良いかもしれません。 野生のポケモン(変数)は戦闘を終えたらガベージコレクションによって解放されて、そのポケモン(変数)は使えなくなってしまいますが、モンスターボール(関数という名のクロージャー)に入れてしまえば、その野生のポケモン(変数)は解放されずに再利用できます。
静的スコープ
const x = 10; // *1 function printX() { // この識別子`x`は常に *1 の変数`x`を参照する console.log(x); // => 10 } function run() { const x = 20; // *2 printX(); // 常に10が出力される } run();
- printXの関数スコープに変数xが定義されていない
- ひとつ外側のスコープ(グローバルスコープ)を確認する
- ひとつ外側のスコープにconst x = 10;が定義されているので、識別子xはこの変数を参照する
上記の流れとなり、run関数内でconst x = 20; を定義したとしても、printXの返り値に影響はありません。コラムで書いてありましたが、動的スコープの言語では影響してしまい、printXの返り値は20になります。
メモリ管理の仕組み
let x = "before text"; // 変数`x`に新しいデータを代入する x = "after text"; // このとき"before text"というデータはどこからも参照されなくなる // その後、ガベージコレクションによってメモリ上から解放される
after textという値が再代入されるタイミングでberore textという値はこのガベージコレクション(ガベージは日本語でゴミという意味ですので認識しておくと覚えやすいです)によってメモリ上から解放されるわけです。 特に意識せずともこの仕組みは理解しているのではないかと感じました。
関数の場合
function printX() { const x = "X"; console.log(x); // => "X" } printX(); // この時点で`"X"`を参照するものはなくなる -> 解放される
関数を呼び出してXを参照したときに解放されます。 しかし、関数には解放されないパターンもあります。
function createArray() { const tempArray = [1, 2, 3]; return tempArray; } const array = createArray(); console.log(array); // => [1, 2, 3] // 変数`array`が`[1, 2, 3]`という値を参照している -> 解放されない
関数の呼び出しをarrayという定数に代入しています。 この場合、関数内のtempArrayの中身の配列のメモリはこの関数を呼び出した後でも、arrayという定数を出力する事で残っているので、ガベージコレクションは働いていないパターンとなります。
この2つを理解した上でprimerでは以下の記述があります。
クロージャーは「静的スコープ」と「参照され続けている変数のデータが保持される」という2つの性質によって成り立っています。
JavaScriptの関数は静的スコープとメモリ管理という2つの性質を常に持っています。そのため、ある意味ではすべての関数がクロージャーとなりますが、ここでは関数が特定の変数を参照することで関数が状態を持っていることを指します。
つまり、JSにおいては全ての関数がモンスターボールの性質を持っているという事です。