「なぜか値が取れない」を防ぐ。変数シャドーイングの正体と、現場で生き残るための作法
フロントエンド開発の現場で、「あれ、この変数の値、さっき定義したはずなのに書き換わってないぞ?」とデバッガを睨みつけた経験はないだろうか。
JavaScriptを触っていると一度は遭遇する「変数シャドーイング(Variable Shadowing)」。これは仕様上の挙動としては正しいものの、コードの可読性を著しく下げ、バグの温床になりやすい厄介な代物だ。
今日は、中級エンジニアの君たちに向けて、シャドーイングの裏側で何が起きているのか、そして「事故らないコード」を書くための思考プロセスを伝授しよう。
—
シャドーイングとは何か:内側のスコープによる「隠蔽」
シャドーイングとは、内側のスコープで外側のスコープと同じ名前の変数を再定義した際、内側の変数が外側の変数を一時的に見えなくする(覆い隠す)現象のことだ。
JavaScriptエンジンは、変数を参照する際、現在のスコープから始まって外側(上位スコープ)へと順に名前を探しにいく。最初に見つかった時点で探索を終了するため、内側に同じ名前があれば、外側の変数は「存在しないもの」として扱われる。
const userRole = ‘admin’; // 外側のスコープ
function printRole() {
const userRole = ‘guest’; // ここでシャドーイング発生!
// 内側のスコープでは ‘guest’ が参照される
console.log(`現在の役割: ${userRole}`);
}
printRole();
console.log(`外側の役割: ${userRole}`); // こちらは ‘admin’ のまま
この例は単純だが、コードが数千行規模になり、ブロック文(`if`や`for`)が入れ子になった瞬間に、この挙動は「予期せぬバグ」として牙を剥く。
—
なぜ「var」は危険なのか:巻き上げとの合わせ技
`var`を使っている現場は今すぐ撲滅すべきだが、歴史的経緯で残っているレガシーコードには注意が必要だ。`var`はブロックスコープを無視するため、シャドーイングがより意図しない形で発生する。
var status = ‘active’;
if (true) {
var status = ‘suspended’; // ブロックスコープを突き抜けて外側を上書きしてしまう
console.log(status); // ‘suspended’
}
console.log(status); // ‘suspended’ に書き換わっている!
`let`や`const`を使えばブロックスコープに閉じ込めることができるが、`var`は関数スコープしかないため、意図せず外側の変数を破壊してしまう。これが「`var`を使うな」と言われる最大の理由だ。
—
現場で実践すべき「シャドーイング事故」回避のベストプラクティス
現場でレビューをする際、私は以下の3点を徹底して守るように伝えている。
1. スコープを可能な限り小さく保つ
変数の寿命が長ければ長いほど、どこかで名前が衝突するリスクは増える。関数を細かく分割し、必要な場所で必要な変数を定義する。これが基本だ。
2. 名前付けの解像度を上げる
「`data`」や「`value`」のような汎用的な名前は、シャドーイングの最大の標的だ。
- `const userData`
- `const responseData`
- `const inputValue`
このように、「何を指しているのか」を名前に含めるだけで、衝突のリスクは劇的に減る。
3. 静的解析(ESLint)に頼り切る
人間はミスをする。だからこそ、機械にルールを強制させるのがプロのやり方だ。ESLintの `no-shadow` ルールを有効にしておこう。
// .eslintrc.json の設定例
{
“rules”: {
“no-shadow”: [“error”, { “builtinGlobals”: false, “hoist”: “functions”, “allow”: [] }]
}
}
これをONにするだけで、意図しないシャドーイングをCIの段階で検知できる。
—
実務での一歩先行くヒント:ループ処理での事故
特に注意が必要なのが、`forEach`や`for…of`の中でのシャドーイングだ。
const items = [{ id: 1 }, { id: 2 }];
function processItems() {
const id = ‘GLOBAL_ID’; // 外側の変数
items.forEach((item) => {
// ここで item.id にアクセスすべきなのに、
// うっかり const id = item.id と書いてしまうと、
// 外側の id が隠蔽され、以降の処理でバグを引き起こす
const id = item.id;
console.log(`処理中: ${id}`);
});
}
こういうときは、「あえて名前を変える」勇気を持つこと。`itemId`や`currentId`など、一目で何の値か分かる名前にリネームするだけで、デバッグの時間は大幅に短縮される。
—
まとめ:変数は「名前」であり「責任」である
変数シャドーイングは、JavaScriptという言語の柔軟性ゆえの「副作用」だ。しかし、この挙動を理解し、適切に制御することは、君のコードの信頼性を確実に一段階引き上げる。
- `var`は使わない。
- 変数はなるべく短命にする。
- 名前の衝突は、ツール(ESLint)と命名規則で防ぐ。
これらを守るだけで、君の書くコードはぐっと「プロフェッショナル」なものになるはずだ。明日からのコーディングで、ぜひ意識してみてほしい。何かあれば、いつでもコードを見せてくれ。現場からは以上だ。

コメント