varとletの違いって何?JavaScriptの変数宣言を完全理解
JavaScriptのvarとletの違いを初心者向けに分かりやすく解説。スコープの違い、ホイスティング、再宣言の仕組みを実例付きで学びましょう。
みなさん、JavaScriptを書いていてこんな経験はありませんか?
「varとletって何が違うの?」「どっちを使えばいいか分からない」「なんでエラーが出るの?」
こんな悩み、実はとても多いんです。 varとletは両方とも変数を作るためのキーワードですが、実は大きな違いがあります。
この記事では、JavaScriptのvarとletの違いについて初心者向けに分かりやすく解説します。 スコープやホイスティングなど、ちょっと難しそうな概念も実例付きで理解していきましょう!
varとletの基本的な違いを知ろう
どちらも変数を作るためのキーワード
まずは基本的な使い方を見てみましょう。
// var による変数宣言var name = "太郎";var age = 25;
// let による変数宣言let email = "taro@example.com";let isActive = true;
console.log(name); // "太郎"console.log(age); // 25console.log(email); // "taro@example.com"console.log(isActive); // true
この例だけ見ると、varもletも同じように動いているように見えますね。 でも実は、内部では大きな違いがあるんです。
主な違いをざっくり理解しよう
varとletの主な違いをまとめてみました。
特徴 | var | let |
---|---|---|
どこまで使える? | 関数全体 | ブロック内だけ |
宣言前に使える? | 使える(undefinedになる) | エラーになる |
同じ名前で再宣言 | できる | エラーになる |
この表を見ながら、詳しく学んでいきましょう。
スコープの違いが一番重要
varは関数全体で使える
varで宣言した変数は、関数全体でアクセスできます。
function varExample() { if (true) { var message = "Hello from var"; console.log(message); // "Hello from var" } // ifブロックの外でも使える! console.log(message); // "Hello from var"}
varExample();
if
ブロックの中で作ったmessage
が、外でも使えているのが分かりますね。
function calculateTotal() { var total = 0; for (var i = 0; i < 5; i++) { var currentValue = i * 10; total += currentValue; } // ループの外でも変数が使える console.log("最後のi:", i); // 5 console.log("最後のcurrentValue:", currentValue); // 40 console.log("合計:", total); // 100}
calculateTotal();
ループが終わった後でもi
やcurrentValue
が使えています。
letはブロック内だけで使える
letで宣言した変数は、そのブロック({}
で囲まれた範囲)内でしか使えません。
function letExample() { if (true) { let message = "Hello from let"; console.log(message); // "Hello from let" } // ifブロックの外では使えない // console.log(message); // ReferenceError: message is not defined}
letExample();
ブロックの外でmessage
を使おうとするとエラーになります。
function calculateTotalLet() { let total = 0; for (let i = 0; i < 5; i++) { let currentValue = i * 10; total += currentValue; } // ループの外では変数にアクセス不可 // console.log("最後のi:", i); // ReferenceError // console.log("最後のcurrentValue:", currentValue); // ReferenceError console.log("合計:", total); // 100}
calculateTotalLet();
ループ用の変数i
やcurrentValue
は、ループの中でしか使えません。
実際によくある問題
ボタンのクリックイベントでよく起こる問題を見てみましょう。
// varを使った場合の問題for (var i = 0; i < 3; i++) { setTimeout(function() { console.log("varループ:", i); // 3, 3, 3(全部3になってしまう) }, 100);}
// letを使った正しい例for (let j = 0; j < 3; j++) { setTimeout(function() { console.log("letループ:", j); // 0, 1, 2(期待通りの結果) }, 200);}
varだと全部同じ値になってしまいますが、letなら期待通りに動きます。
ホイスティングの違いを理解しよう
varは宣言前でも使える
ホイスティングとは、変数の宣言が自動的に上に持ち上げられる仕組みです。
// 実際のコードconsole.log(varVariable); // undefined(エラーではない)var varVariable = "Hello";console.log(varVariable); // "Hello"
宣言前にvarVariable
を使ってもエラーになりません。
これは、JavaScriptが内部で次のように解釈するからです。
// JavaScriptが内部で解釈する形var varVariable; // undefined で初期化されるconsole.log(varVariable); // undefinedvarVariable = "Hello";console.log(varVariable); // "Hello"
変数の宣言だけが上に移動して、最初はundefined
になるんです。
letは宣言前に使うとエラーになる
letの場合は、宣言前にアクセスするとエラーになります。
// letの場合はエラーになる// console.log(letVariable); // ReferenceError: Cannot access 'letVariable' before initializationlet letVariable = "Hello";console.log(letVariable); // "Hello"
これは一時的なデッドゾーンという仕組みのためです。
function demonstrateDeadZone() { console.log("関数開始"); // この範囲では x にアクセスできない(デッドゾーン) // console.log(x); // ReferenceError let x = 10; console.log("初期化後のx:", x); // 10}
demonstrateDeadZone();
変数が宣言されてから初期化されるまでの間は、アクセスできないエリアになっています。
再宣言の違いも重要
varは同じ名前で何度でも宣言できる
varなら、同じ変数名で何度でも宣言できます。
// varは再宣言が可能var username = "太郎";console.log(username); // "太郎"
var username = "花子"; // エラーにならないconsole.log(username); // "花子"
var username = "次郎";console.log(username); // "次郎"
エラーにはなりませんが、うっかり同じ名前を使ってしまう危険があります。
letは同じ名前での再宣言は不可
letでは、同じスコープ内で同じ名前の変数を再宣言するとエラーになります。
// letは再宣言不可let email = "user@example.com";console.log(email); // "user@example.com"
// let email = "another@example.com"; // SyntaxError: Identifier 'email' has already been declared
// ただし、値の変更は可能email = "updated@example.com";console.log(email); // "updated@example.com"
同じ名前での宣言はエラーになりますが、値の変更は問題ありません。
// 異なるブロック(スコープ)なら再宣言可能function letRedeclaration() { let message = "外側のメッセージ"; console.log("外側:", message); // "外側のメッセージ" if (true) { let message = "内側のメッセージ"; // 異なるスコープなので可能 console.log("内側:", message); // "内側のメッセージ" } console.log("外側(再度):", message); // "外側のメッセージ"}
letRedeclaration();
異なるブロック内なら、同じ変数名でも宣言できます。
実践的な使い方を学ぼう
ループでの正しい使い方
配列の要素にイベントリスナーを追加する例です。
// タブメニューの作成例function createTabMenu() { let tabs = ["ホーム", "サービス", "お問い合わせ"]; for (let index = 0; index < tabs.length; index++) { let button = document.createElement("button"); button.textContent = tabs[index]; // 各ボタンが正しいインデックスを保持 button.addEventListener("click", function() { console.log(`${tabs[index]}が選択されました`); console.log(`インデックス: ${index}`); }); document.body.appendChild(button); }}
letを使うことで、それぞれのボタンが正しいインデックスを覚えてくれます。
条件分岐での変数管理
ユーザーの年齢に応じて処理を変える例です。
function processUserData(user) { if (user.age >= 18) { let accessLevel = "adult"; let permissions = ["read", "write", "delete"]; console.log(`アクセスレベル: ${accessLevel}`); console.log(`権限: ${permissions.join(", ")}`); if (user.role === "admin") { let adminPermissions = [...permissions, "admin"]; console.log(`管理者権限: ${adminPermissions.join(", ")}`); // adminPermissionsはこのブロック内でのみ有効 } } else { let accessLevel = "minor"; let permissions = ["read"]; console.log(`アクセスレベル: ${accessLevel}`); console.log(`権限: ${permissions.join(", ")}`); } // accessLevelやpermissionsはここではアクセス不可}
// 使用例processUserData({ age: 25, role: "user" });processUserData({ age: 16, role: "user" });processUserData({ age: 30, role: "admin" });
ブロックごとに適切な変数を使い分けられます。
現代JavaScriptでの推奨事項
letとconstを使おう
現代のJavaScriptでは、varの代わりにletとconstを使うことが推奨されています。
// 推奨:letとconstの使用function modernVariableDeclaration() { // 変更されない値はconst const API_URL = "https://api.example.com"; const MAX_RETRY_COUNT = 3; // 変更される値はlet let currentRetry = 0; let response = null; // varは使用しない(レガシーコード以外) while (currentRetry < MAX_RETRY_COUNT) { try { console.log(`試行回数: ${currentRetry + 1}`); // 何らかの処理 break; } catch (error) { currentRetry++; console.log(`リトライ ${currentRetry}/${MAX_RETRY_COUNT}`); } }}
constは値が変わらない場合、letは値が変わる場合に使います。
使い分けのルール
以下のルールに従うと良いでしょう。
// 良い例function goodPractice() { const users = ["太郎", "花子", "次郎"]; // 配列自体は変更しない let selectedUser = null; // 値が変更される for (const user of users) { // ループ変数も変更しない if (user === "花子") { selectedUser = user; break; } } console.log("選択されたユーザー:", selectedUser);}
// 避けるべき例function badPractice() { var users = ["太郎", "花子", "次郎"]; // var は使わない var selectedUser = null; // var は使わない for (var i = 0; i < users.length; i++) { // var は使わない if (users[i] === "花子") { selectedUser = users[i]; break; } } console.log("選択されたユーザー:", selectedUser);}
一貫してletとconstを使うことで、より安全なコードが書けます。
よくある間違いと対策
varとletの混在は避けよう
同じコードでvarとletを混在させると混乱の原因になります。
// 悪い例:varとletの混在function mixedDeclaration() { var oldStyle = "古い書き方"; let newStyle = "新しい書き方"; // 一貫性がなく、混乱の原因となる for (var i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { console.log(i, j); } }}
// 良い例:一貫してletとconstを使用function consistentDeclaration() { const message = "一貫した書き方"; let counter = 0; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { counter++; console.log(`${message}: ${i}, ${j} (${counter})`); } }}
統一された書き方にすることで、読みやすいコードになります。
ホイスティングを理解しよう
ホイスティングを正しく理解することで、予期しないエラーを防げます。
// 正しい理解と対策function safeVariableUsage() { // 変数は使用前に必ず宣言・初期化 const config = { apiUrl: "https://api.example.com", timeout: 5000 }; let result = null; let error = null; try { result = processData(config); } catch (e) { error = e; } if (error) { console.log("エラーが発生しました:", error.message); } else { console.log("処理結果:", result); }}
function processData(config) { return "処理完了"; // 模擬処理}
変数を使う前に必ず宣言と初期化を行うことで、安全なコードが書けます。
パフォーマンスのメリット
ブロックスコープによるメモリ効率
letのブロックスコープを活用することで、メモリを効率的に使えます。
// ブロックスコープを活用したメモリ効率の向上function processLargeData() { const data = new Array(1000).fill(0).map((_, i) => i); // 前処理ブロック { let tempArray = data.map(x => x * 2); let processedData = tempArray.filter(x => x % 10 === 0); console.log("前処理完了:", processedData.length); // tempArrayはここでメモリから解放される } // メイン処理ブロック { let result = data.reduce((sum, value) => sum + value, 0); console.log("合計:", result); // resultはここでメモリから解放される } console.log("処理完了");}
必要な時だけ変数を作って、不要になったらすぐに解放されるようにできます。
まとめ
JavaScriptのvarとletの違いについて学んできました。
主な違いをもう一度おさらいしましょう。
- スコープ: var(関数全体)vs let(ブロック内のみ)
- ホイスティング: var(undefined初期化)vs let(デッドゾーン)
- 再宣言: var(可能)vs let(エラーになる)
実践的な使い方も覚えておきましょう。
- ループ処理ではletを使う
- 条件分岐でのブロックスコープを活用
- constとletを適切に使い分ける
- varの使用は避ける
現代のベストプラクティスとして以下を推奨します。
- 新しいコードではvarを使わない
- 値が変わらない場合はconst
- 値が変わる場合はlet
- 一貫した書き方を心がける
varとletの違いを理解することで、より安全で読みやすいJavaScriptコードが書けるようになります。 最初は少し複雑に感じるかもしれませんが、慣れれば自然に使い分けられるようになりますよ。
ぜひ今度のプロジェクトで、letとconstを使ってみませんか?