let とは何か
let は ES6 で導入された“ブロックスコープの再代入可能な変数宣言”です。ここが重要です:let は「宣言されたブロック内でのみ有効」「同じスコープで再宣言できない」「宣言前に使えない(Temporal Dead Zone)」という性質を持ちます。従来の var の“関数スコープ+巻き上げ(hoisting)による曖昧さ”を解消し、意図どおりに安全な変数管理ができます。
{
let x = 1;
console.log(x); // 1
}
console.log(typeof x); // "undefined"(ブロック外では存在しない)
JavaScriptスコープの違い(ブロックスコープ vs 関数スコープ)
let はブロックに閉じる
let は { } で囲まれたブロックに閉じます。if、for、try、switch などの中で宣言した let は、そのブロック外から見えません。ここが重要です:意図しない“外側の上書き”や“漏れ”を防げます。
if (true) {
let a = 10;
}
// a はここでは未定義
JavaScriptvar は関数に閉じる(比較用)
var は“関数スコープ”なので、ブロックをまたいで見えることがあり、意図せず値が共有されます。実務では let を標準にし、var は使わない方針が安全です。
if (true) {
var v = 10;
}
console.log(v); // 10(ブロック外でも見える)←事故の元
JavaScriptTemporal Dead Zone(宣言前は使えない)
宣言前アクセスで例外
let は宣言位置まで変数が“存在しないゾーン”にあり、参照・代入ができません。この区間を TDZ(Temporal Dead Zone)と呼びます。ここが重要です:let を“宣言してから使う”癖をつけると、意図しない undefined 使用を防げます。
console.log(x); // ReferenceError(TDZ)
let x = 1;
JavaScriptデフォルト引数や内側ブロックでも TDZ
内側ブロックで外側の同名変数を“再宣言”すると、宣言前の行は TDZ になります。シャドーイングと TDZの組み合わせに注意しましょう。
let n = 5;
{
// console.log(n); // ReferenceError(このブロック内で n を再宣言予定のため TDZ)
let n = 10;
console.log(n); // 10
}
JavaScript再宣言不可・再代入可(const との対比)
同じスコープで再宣言はできない
let は同じスコープで同じ名前を再宣言すると SyntaxError になります。ここが重要です:重複宣言をコンパイル時に防げるので、保守性が上がります。
let a = 1;
// let a = 2; // SyntaxError(同スコープで再宣言不可)
JavaScript値の再代入はできる(const は不可)
let は値を後から更新できます。const は“再代入不可”なので、基本は const を使い“変更が必要なときだけ let”を選ぶのが実務のベストプラクティスです。
let count = 0;
count += 1; // OK
const limit = 10;
// limit = 11; // TypeError(const は再代入不可)
JavaScriptループとクロージャでの利点(初心者が躓くポイントの解決)
setTimeout とインデックスの捕捉が意図どおりに働く
var はループ後の最終値を共有しがちですが、let は“各反復ごとに新しい束縛”になり、期待どおりの値がクロージャに閉じ込められます。ここが重要です:非同期処理でインデックスを正しく扱えることが、let の最大の恩恵のひとつです。
for (let i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), 0); // 1, 2, 3 と順に出る
}
// var だと全て 4 になってしまう典型的事故
for (var j = 1; j <= 3; j++) {
setTimeout(() => console.log(j), 0); // 4, 4, 4
}
JavaScriptfor 文ヘッダの let は“ブロック限り”
for (let i = 0; i < 3; i++) の i はループブロックに限定され、外から見えません。意図しないスコープ汚染が防げます。
for (let i = 0; i < 2; i++) {}
// console.log(i); // ReferenceError(スコープ外)
JavaScriptグローバルと let(安全な名前空間)
グローバルオブジェクトにぶら下がらない
ブラウザの var グローバルは window にプロパティとして現れますが、let のグローバルは window に現れません。ここが重要です:名前衝突を避け、安全にグローバルスコープを使えます(とはいえ、グローバル乱用は避けるべき)。
var gv = 1;
let gl = 2;
console.log(window.gv); // 1
console.log(window.gl); // undefined(let はぶら下がらない)
JavaScriptswitch 文の落とし穴と対策
switch は単一ブロック扱い(case ごとの let 再宣言は不可)
switch は全体が一つのブロックです。同名の let を複数の case で宣言すると再宣言エラーになります。ここが重要です:case ごとに { } でブロックを作り、宣言を分けるのが安全です。
switch (type) {
case "A": {
let x = 1;
console.log(x);
break;
}
case "B": {
let x = 2; // 別ブロックなので OK
console.log(x);
break;
}
}
JavaScript実務での使い分け(指針)
原則は const、必要なときだけ let
不変が正義です。まず const を試し、“後で変える必要がある”と分かったら let にする。ここが重要です:不変にするとバグが減り、意図が明確になります。let は「カウンタ」「集計の累積」「一時変数の更新」に限定すると読みやすくなります。
const items = [1, 2, 3]; // 変わらない一覧
let sum = 0; // 累積は let
for (const n of items) sum += n;
JavaScript宣言は最小スコープに置く
必要な場所で宣言し、不要な広いスコープに漏らさない。TDZ と合わせて「宣言してから使う」を徹底すると、未初期化の事故を避けられます。
function f() {
// 使う直前で宣言・初期化
let tmp = compute();
return tmp * 2;
}
JavaScript例題で理解を固める
let のシャドーイングと外側の保護
内側で同名を宣言しても外側は守られます。ここが重要です:意図的に“局所的な上書き”がしたいとき、let で安全に閉じ込められます。
let label = "outer";
{
let label = "inner";
console.log(label); // "inner"
}
console.log(label); // "outer"
JavaScriptTDZ がバグを先に見つける例
未宣言のまま使ってしまうコードを、その場で止めます。“静かな undefined”より安全です。
function calc() {
// console.log(total); // ReferenceError(TDZ)
let total = 0;
return total;
}
JavaScriptまとめ
let の核心は「ブロックスコープ」「宣言前不可(TDZ)」「再宣言不可・再代入可」です。これにより、従来の var の曖昧さ(巻き上げ・スコープ漏れ・クロージャ事故)を解消できます。実務では“まず const、必要なら let”を原則にし、宣言は最小スコープへ、switch は case をブロックで囲む。非同期やループのインデックス捕捉で let を使うと、期待どおりに動き、初心者でもバグを避けながら読みやすいコードを書けます。

