変数解決とは何か(どの値を使うかを決める仕組み)
「変数解決」とは、コードの中で名前(例:count, user)を使ったときに、JavaScript が「その名前が指す具体的な値」をどのスコープから見つけるかを決める手順のことです。重要なのは、JavaScriptは「定義された位置」に基づくレキシカルスコープを使い、内側から外側へ段階的に探します。呼び出し位置ではなく、定義位置で探索の起点が決まる点が本質です。
探索の順序(スコープチェーンの流れ)
変数名を解決するときの基本の順番は「一番近いところから、外へ外へ」です。直感は「今いる部屋から廊下、さらに外の建物へ」という階層移動。
現在のブロック(TDZに注意)
let/const で宣言された同名がそのブロック {} 内にあれば最優先で使われます。ただし、初期化前(TDZ)に触ると ReferenceError になります。近いものが優先されるが、準備が整っていないと使えない、というルールです。
関数のローカルスコープ
ブロックで見つからなければ、関数内の他のブロックや関数の引数・ローカル変数を探します。ここで同名が見つかればそれが使われます(シャドーイングの典型)。
外側の関数やモジュールのスコープ
さらに外側の関数で宣言された変数、またはモジュール(ファイル)トップレベルの変数を探します。レキシカルに「定義された位置の外側」へ広がるのがポイントです。
最後にグローバルスコープ
どこにもなければ、グローバルスコープ(ブラウザならほぼ window、ESMではモジュール単位で分離)まで探します。それでもなければ ReferenceError です。
例題で手触りをつかむ(近いほうが勝つ・定義位置が効く)
例題1:内側が外側を隠す(シャドーイング)
const name = "太郎"; // グローバル
function greet() {
const name = "花子"; // 関数内(近いほう)
console.log(name); // 花子
}
greet();
console.log(name); // 太郎
JavaScript同名が内側にあれば、外側は隠されます。探索は「内→外」の順です。
例題2:ブロック → 関数 → グローバルの順で探す
const value = "global";
function demo() {
const value = "function";
if (true) {
const value = "block";
console.log(value); // block(最も近い)
}
console.log(value); // function(次に近い)
}
demo();
console.log(value); // global(外の最後)
JavaScript一番近いブロックから、関数、グローバルの順で決まります。
例題3:呼び出し場所は関係ない(レキシカルスコープ)
const a = "global A";
function outer() {
const a = "outer A";
function inner() {
console.log(a);
}
return inner;
}
const fn = outer();
fn(); // outer A
JavaScriptinner は「outerの中で定義された」ため、呼び出し場所がグローバルでも「outerのA」を見ることになります。
TDZ と var の違い(未初期化の扱いを深掘り)
let/const は宣言行までの間は TDZ にあり、参照すると ReferenceError。var は関数スコープで、宣言前でも undefined として読めます。
console.log(x); // ReferenceError(TDZ)
let x = 1;
function f() {
console.log(y); // undefined(varは巻き上げ)
var y = 2;
}
f();
JavaScript重要な直感: 「エラーで止まる方(TDZ)のほうが、未初期化のまま計算に混ざる事故を防げる」。基本は let/const を使い、宣言・初期化を使用より前に置く習慣を持つと安全です。
引数と同名の外側変数(優先順位の深掘り)
関数の引数は、その関数の最も近いスコープに属します。外側の同名変数より引数が優先されます。さらに、デフォルト引数の評価は「引数リスト内のスコープ」で行われるため、同名を参照するとTDZに落ちやすい点に注意。
const rate = 0.1;
// 引数が優先される例
function priceWithTax(price, rate) {
return Math.floor(price * (1 + rate));
}
console.log(priceWithTax(1000, 0.08)); // 1080
// デフォルト引数の落とし穴(避けるべき)
function bad(price, rate = rate) { // ここで内側のrate(未初期化)を参照
return price;
}
// bad(1000); // ReferenceError
JavaScript安全策は「別名の定数を用意して参照する」か「引数名を外側と衝突させない」ことです。
モジュールとグローバルの境界(探索の終点を理解する)
ES Modules では、ファイルのトップは「モジュールスコープ」であり他のファイルと自動共有されません。変数解決の最終地点は「そのモジュールの外側(グローバル)」ですが、通常は export/importで明示的に共有します。無意識にグローバルへ依存せず、必要なものは import して使うと、探索の終点が明確になりバグを防げます。
// config.js
export const TAX_RATE = 0.1;
// calc.js
import { TAX_RATE } from "./config.js";
export function priceWithTax(p) { return Math.floor(p * (1 + TAX_RATE)); }
JavaScriptまとめ(安全な変数解決のための感覚)
変数解決は「内側から外側へ」段階的に探す仕組みで、定義位置(レキシカル)がすべての起点になります。近いスコープが優先され、let/const は初期化前に触るとエラー(TDZ)、引数は最も近いスコープとして外側より優先されます。宣言を使用より前に置く、同名衝突を避ける、モジュールで明示的に共有する——この3つを守るだけで、変数解決は直感的になり、予測可能で安全なコードを書けるようになります。

