JavaScript レキシカルスコープとは?
クロージャの一種
公式の解説によると
クロージャ
クロージャは、組み合わされた(囲まれた)関数と、その周囲の状態(レキシカル環境)への参照の組み合わせです。言い換えれば、クロージャは関数にその外側のスコープにアクセスする機能を提供します。JavaScript では、クロージャは関数が作成されるたびに、関数作成時点で作成されます。
レキシカルスコープ
次のような関数を考えてみてください。
function init() {
var name = "Mozilla"; // name は、init が作成するローカル変数
function displayName() {
// displayName() は内部に閉じた関数
console.log(name); // 親関数で宣言された変数を使用
}
displayName();
}
init();
init() 関数はローカル変数 name を作成し、それから関数 displayName() を定義しています。displayName() は init() の中で定義されている内部関数で、その関数本体の内部でしか利用できません。displayName() 自体はローカル変数を持っていませんが、外側のスコープで宣言された変数にアクセスできるので、displayName() では親関数 init() で宣言された変数 name を使用できます。しかし、 displayName() に同じローカル変数があればそれが使われます。
この JSFiddle リンクを使用してコードを実行すると、displayName() 関数内の console.log() 文が、その親関数で宣言されている name 変数の値を正常に表示していることに注意してください。これはレキシカルスコープの例で、関数が入れ子になっているときにパーサーがどのように変数名を解決するかを記述したものです。レキシカルという言葉は、レキシカルスコープがソースコード内で変数が宣言された場所を使用して、その変数が利用できる場所を決定するという事実を示しています。入れ子の関数は、その外側のスコープで宣言された変数にアクセスすることができます。
MDNの解説わかりにくい
難しい言葉のように超える人もいるかも
違う表現だと...
例を見てみましょう:
function init() {
var name = "Mozilla"; // name はinit関数内のローカル変数
function displayName() { // displayNameはinit内の内部関数
console.log(name); // 親関数のnameを使用できる
}
displayName();
}
init(); // "Mozilla"を表示
この例では:
-
init()
関数の中にname
というローカル変数があります -
init()
の中にdisplayName()
という内部関数を定義しています -
displayName()
関数は自分の中にname
変数を持っていませんが、親関数であるinit()
のname
変数にアクセスできます
これがレキシカルスコープの特徴です:関数は自分が定義された環境の変数にアクセスできます。つまり、内側の関数は外側の関数の変数を「見る」ことができます。
もしdisplayName()
内部にもname
変数があれば、その内部の変数が優先されます:
function init() {
var name = "Mozilla";
function displayName() {
var name = "Firefox"; // 自分の変数を優先
console.log(name);
}
displayName();
}
init(); // "Firefox"を表示
レキシカルスコープは、コードを書いた「場所」によって変数のアクセス範囲が決まるため「静的スコープ」とも呼ばれます。これは実行時の状況ではなく、コードの構造によって決まるのが特徴です。
公式以外の使用例知りたく作ってみた
カウンターですね。
function createCounter() {
let count = 0; // 外部関数のローカル変数
return () => { // 内部関数がクロージャを形成
count++; // 外部関数のcount変数にアクセス
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
ボタンをクリックするイベントが発生したら実行される関数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="submitButton">送信</button>
<button id="cancelButton">キャンセル</button>
</body>
<script>
function setupButton(buttonId, message) {
const button = document.getElementById(buttonId);
button.addEventListener('click', (() => {
// 外部スコープの変数messageにアクセス
console.log(message);
}));
}
setupButton('submitButton', 'フォームが送信されました');
setupButton('cancelButton', '操作がキャンセルされました');
</script>
</html>
まとめ
レキシカルスコープ:コード内での変数の参照範囲を決める仕組みで、変数や関数が宣言された「場所」によって決まります。関数が他の関数の中で定義されているとき、内側の関数は外側の関数の変数にアクセスできるという性質です。
Discussion