【JavaScript】varとletの違いとは?初心者向けにわかりやすく解説
はじめに
JavaScriptで変数を宣言する方法として、var と let があります。
初心者の方にとっては「同じように変数を作るのに、わざわざ2つの書き方があるのはなぜだろう?」という疑問を感じるかもしれません。
この記事では、それぞれの書き方がどう違うのか、どのようなメリット・デメリットがあるのか、そして実務における使い分け方を紹介します。
初めてJavaScriptに触れる方でも理解しやすいように、なるべく平易な言葉で具体例とともに解説を進めていきますので、ぜひ最後まで読んでみてください。
この記事を読むとわかること
- varとletの基本的な違い
- それぞれのスコープや再宣言の挙動
- 実際の開発現場での使い分け方
- 具体的なコード例をもとにした活用シーン
ここで挙げる解説を理解すると、JavaScriptで変数をどのように宣言するか悩んだときに、どちらを使うべきか判断しやすくなります。
varとletの基本的な特徴
JavaScriptは比較的自由度が高い言語で、変数の宣言方法もいくつか存在しています。
まずは、なぜJavaScriptに複数の宣言方法があるのか、どのように進化してきたのかを大まかに理解することから始めましょう。
varの登場背景
var は、JavaScriptの初期から存在する変数宣言の方法です。
古くから使われていたため、多くの既存コードやチュートリアルなどでも目にする機会があります。
ただし、後述するようにスコープの扱いが少し独特で、ブロック単位ではなく関数単位で管理されることが特筆すべき点です。
このため、予期せず変数が上書きされてしまう といったトラブルが起こることがありました。
こうした問題を解消するために、ES2015(ECMAScript 6)で let が導入されたという経緯があります。
letの登場背景
let は、ブロックスコープを意識した新しい変数宣言の方法です。
varのスコープにまつわる問題点を解消するために導入されました。
加えて、再宣言が許可されない仕様となっているので、うっかり同じ変数名で宣言してしまい、既存の値を上書きするようなエラーを防げます。
例えば、小規模なサンプルコードでは問題にならなくても、チームでの大規模開発や複数人で保守していく段階になると、「意図せず変数が競合してバグが発生する」 といったリスクは高まるでしょう。
このようなリスクを減らすためにも、letは多くの開発者に好まれて使われるようになってきました。
varとletの実務での活用シーン
実際の現場で、プログラマーたちはvarとletをどのように使い分けているのでしょうか。
ここでは、開発シーンにおける具体的な例とともに、その使い道を見ていきます。
小規模スクリプトと既存コードの修正シーン
過去から存在するプロジェクトにはvarの記述が大量に残っています。
こうしたコードをメンテナンスする際、必要最小限の修正で済ませたい場合は、既存の流れに合わせてvarを使うこともあります。
一方、機能追加や改善を行う段階で、コードの品質を高めたいときは、可能な範囲でletやconst(同じくES2015で導入された、再代入を禁止する宣言)を取り入れることも検討されるでしょう。
ブロックスコープが重要な場面
たとえば、以下のようにループや条件分岐が多用される場面では、ブロックスコープを持つletを使うと、意図しない変数の上書きを防ぐことができます。
for (let i = 0; i < 5; i++) { console.log(i); } // ここでiを参照しようとするとエラーになる
上記のように、ブロック({ ... }
)内でのみ有効な変数にしたいなら、letを使うのが一般的です。
もしvarを使って同様の書き方をした場合、ループの外側でもiが存在してしまうため、思わぬバグを招くことがあります。
チーム開発でのコーディング規約
チーム開発では、スタイルガイドやコーディング規約が存在することが多いです。
その多くで「変数宣言は原則としてletを使用し、再代入が不要な場合はconstを使う」などと定義されているケースが増えてきました。
これにより、varは使用しない という方針が浸透しつつあります。
ただし、古い仕様に依存したツールやライブラリとの互換性などを理由に、まだvarを使わざるを得ない状況もないわけではありません。
プロジェクトの背景によってはvarを混在させることもありますが、基本的にはletの方が推奨される傾向にあります。
varの基本的な挙動
ここからはもう少し具体的に、var の特徴を見ていきましょう。
メリットとデメリットがあるため、それぞれ押さえておくと混乱を減らせます。
varのスコープとhoisting
var で宣言された変数は、関数スコープ を持ちます。
つまり、関数内であればブロック {}
の内外を意識せずに同じ変数として扱われるのです。
例えば、次のコードを見てみましょう。
function example() { if (true) { var x = 10; } console.log(x); // 10 } example();
この例では、if
ブロックの中で宣言したはずの x
が、ブロックの外でも参照できます。
こうした挙動は、C言語やJavaなどのブロックスコープに慣れた方にとっては驚くかもしれません。
さらにhoisting (変数の巻き上げ)という仕組みによって、コードが実行される前に宣言だけが関数の先頭に移動したかのように扱われます。
その結果、スクリプト内のどの位置でもvar変数は定義されていることになるため、「宣言される前に変数にアクセスしてしまった」 というバグを潜ませるリスクが生まれやすいのです。
varの再宣言と上書きのリスク
var は再宣言が許可されます。
すなわち、同じスコープ内で同名の変数を再びvarで宣言してもエラーになりません。
var y = 5; var y = 7; console.log(y); // 7
一見便利そうに見えますが、これにより意図せず変数が上書きされる 問題が起こりやすくなります。
特に、複数人が同時に開発する際などは、命名の衝突が生じてバグを生む一因になるでしょう。
このような理由から、最近のJavaScriptのスタイルガイドでは、開発者の混乱を避けるためにvarの使用が非推奨とされることが増えています。
letの基本的な挙動
続いて、let の特徴を詳しく見てみます。
先ほども触れましたが、varと明確に異なる点がいくつかあるため、特に初心者の方はしっかり把握しておきましょう。
letのスコープとブロック単位の管理
let で宣言された変数は、ブロックスコープ を持ちます。
例えば、以下のコードを見てみましょう。
if (true) { let a = 10; console.log(a); // 10 } // console.log(a); // ここでaにアクセスするとエラーになる
このように、ブロック { ... }
の外側から a
を参照するとエラーが発生します。
ブロック内だけで有効な変数にしたいときに適しており、スコープが明確 です。
誤って外部から上書きされるリスクを下げられるため、大きなプロジェクトや複雑な処理を扱う際に優位性が高いでしょう。
letで再宣言はエラーになる
let は、同一スコープ内で再度宣言するとエラーになります。
以下の例のように、同じ変数名を再度 let
で宣言するとプログラムが停止するので、意図しない上書きを未然に防ぎやすいです。
let b = 1; let b = 2; // エラー
この仕組みによって、変数の名前が重複しないように強制されるため、保守しやすいコードを自然に書けるようになります。
letのhoistingの挙動
letには、 Temporal Dead Zone (TDZ) という概念があります。
変数が宣言される前の領域でその変数にアクセスしようとすると、エラーが出るように仕様上定義されています。
console.log(c); // まだ宣言前なのでエラー let c = 10;
このように、宣言前に変数を使うとエラーになるため、自然と「変数宣言を先に書く」コードスタイルが促進されます。
varと同様にエンジン内部では一度変数が “宣言” されたことにはなるのですが、開発者が変数の正しいスコープを意識しやすくする という点で、letの方が扱いやすいでしょう。
実際のコード例を使った比較
ここでは、短いコード例を通してvarとletの違いを具体的にイメージしてみましょう。
それぞれのスコープの振る舞いや、変数再宣言の禁止などを確認します。
varを用いた例
function varExample() { var result = "初期値"; if (true) { var result = "条件分岐内で変更"; } console.log(result); // 結果は「条件分岐内で変更」 } varExample();
if
ブロック内で var result
が再宣言されてしまったため、ブロック外の result
にも影響を及ぼし、最終的には "条件分岐内で変更" が出力されます。
このように、スコープが関数単位であること、再宣言が許可されることが相まって、意図しない上書き が発生してしまいがちです。
letを用いた例
function letExample() { let result = "初期値"; if (true) { let result = "条件分岐内で変更"; console.log(result); // ここでは「条件分岐内で変更」 } console.log(result); // ここでは「初期値」 } letExample();
こちらはブロック内で宣言した result
とブロック外の result
が別物として扱われます。
こうした挙動は、構造が複雑になるほどプログラムの安全性を高めることにつながります。
開発現場での使い分け方
それでは、実際の現場ではvarとletのどちらをメインに使えばいいのでしょうか。
結論から言うと、 よほどの理由がない限り、let (またはconst) を利用するのが一般的 です。
letを優先する理由
先ほど解説したように、letはブロックスコープを有し、再宣言が禁止されています。
その結果、予期せぬ上書きを避けやすく、変数のスコープ管理がしやすいというメリットがあります。
また、ES2015以降のプロジェクトでは、minification(コード圧縮)や最適化においてもletやconst前提の書き方が進められているケースが多いです。
さらに、モダンフレームワーク(ReactやVueなど)を使う場合も、チュートリアルや公式ドキュメントのほとんどがlet/constを前提にサンプルコードを示していることが多く、開発環境全体がそれに合わせて進化 しているという側面があります。
varを使う理由や場面
ではvarはもう完全に使われなくなったのかというと、いまだにレガシーなコードベースや大規模システムの一部ではvarが残っています。
また、動作環境が特別な場合(ごく古いブラウザや特殊なJavaScript処理系など)には、varでないと都合が悪いこともあり得ます。
しかし、そうしたケースでない限り、新しくコードを書く場合はletを採用する方が安全 です。
既存プロジェクトでvarを使用している場合、段階的にletやconstへ置き換えていくことで、コードの可読性や保守性を高めることができます。
varとletの選択における注意点
ここでは、varとletを選ぶ際に注意したいポイントをいくつかまとめます。
まったくの初心者の方でも、これらの点を押さえておけば混乱を減らせるでしょう。
スコープの概念を優先的に覚える
JavaScriptに限らず、多くのプログラミング言語で「スコープ」は非常に重要な概念です。
特にJavaScriptは、昔からスコープがやや特殊だと言われてきました。
その背景にはvarによる関数スコープ が大きく関わってきます。
一方で、letが導入された今は**「ブロックスコープが標準的な感覚」** になりつつあります。
つまり、他のモダンな言語(例えばC++やJavaなど)と同じ感覚で変数を管理できるので、最初からletを使っておけば、大きなミスは減らせるはずです。
チームのコーディング規約を確認する
もし皆さんが、複数人で開発を行うような現場にいる場合は、まずチームのコーディングルールを確認しましょう。
「変数宣言はletかconstのみを使う」という規約があれば、それに従うのがベストです。
チームで足並みを揃えることで、コードレビューの手間も減り、開発効率が上がります。
既存のコードを無理に書き換えない
既に運用中のサービスでは、大量のJavaScriptコードがvarで書かれている場合があります。
その全てを無理にletやconstに書き換えるとなると、バグの混入リスクやテストコストが生じます。
大きなプロジェクトほど、そのコストも跳ね上がるでしょう。
もちろん、余裕があれば書き換えて保守性を高めるのが理想です。
しかし、皆さんが開発現場で時間や予算の制約がある場合には、「これから追加する新機能部分」や「既存コードを修正するついで」 に少しずつ置き換えていく、というアプローチが現実的といえます。
一斉にvarを置き換える場合は、コードの影響範囲が広いことを考慮し、テストを十分に行ってからリリースしましょう。
よくある疑問やトラブル事例
初心者の方が、varとletを学び始めたときによく感じる疑問や、実務で起こりがちなトラブルをいくつか取り上げてみます。
varとletを混在させるとき
例えば、既存コードではvarが使われているが、新たに書く部分ではletを使いたい、というケースが出てきます。
この場合でも、基本的には問題ありません。
ただし、同じスコープ内で同じ変数名をvarとletで宣言するのはエラーの原因になりやすいので気をつけましょう。
ループで値が意図せず上書きされる
配列の要素をループ処理で扱う際など、for
や forEach
の中で変数が使い回されてしまうケースはよくあります。
特にvarだとループ内部での再代入が外部に影響を及ぼすことがあります。
これが**「値が勝手に変化してしまう」** トラブルの元です。
そんなときには、ブロックスコープをきちんと区切るためにletを使うと、意図しない上書きを防止しやすいでしょう。
デバッグ時の混乱
デバッガーでコードをステップ実行しているとき、「変数がどのスコープに所属しているのか」 を見失いがちです。
特にvarだと、ブロック外でも生きている変数があったりして、一見同じ名前の変数のようで違うものだったり、逆に同じものだったりで混乱します。
これらもletがデフォルトになったことで、ある程度緩和されています。
varとletに関する比較表
ここで一度、まとめとして var と let の主要な違いを一覧表にしておきましょう。
文字ベースで説明された内容を視覚的に再確認すると、さらに理解が深まるかもしれません。
項目 | var | let |
---|---|---|
スコープ | 関数スコープ | ブロックスコープ |
再宣言 | 同一スコープ内で再宣言可能 | 同一スコープ内で再宣言不可 |
hoisting(巻き上げ) | 宣言が巻き上げられる | 一応巻き上げられるがTDZあり |
主要な導入バージョン | ES1(初期から) | ES2015 |
実務での推奨度 | 非推奨(新規では使わない) | 推奨(互換性の問題がなければ使う) |
この表を見ても分かるように、モダンなJavaScriptではletの方が安全 かつ扱いやすいことが分かります。
その他の周辺知識
変数宣言の方法は、JavaScriptのコード品質や可読性に大きく関わります。
ここでは、varとletを理解するうえで合わせて覚えておくと便利なポイントをいくつか紹介します。
constの存在
変数の宣言方法として、const というキーワードもよく使われます。
これは「再代入不可」な変数を宣言するためのキーワードで、初回に代入した値を後から変えることができません。
const PI = 3.14; PI = 3.14159; // エラー
letと同じくブロックスコープを持ちますが、値が変わらない前提の場合に使用されます。
今後、さまざまなサンプルコードを見るときは、varよりもconstやletが中心になるでしょう。
古い環境へのトランスパイル
比較的新しい構文(let、constなど)を使うと、一部の古いブラウザ ではそのまま実行できない場合があります。
しかし、BabelやTypeScriptなどのトランスパイラを使うことで、古い環境でも実行可能な形に変換することが一般的になっています。
そのため、モダンな構文を採用しても問題ないケースが多いでしょう。
linterの導入
開発現場では、ESLintなどの静的解析ツール を使って、コード品質を自動チェックすることがよくあります。
このようなツールを導入すると、varを使った部分について「非推奨」と警告が出る場合があるため、自然とletへの移行を促されることになります。
まとめ
varとletの違いについて、基本的なスコープや再宣言の可否、hoistingなどの特徴を挙げながら解説してきました。
次のポイントを押さえておくと、実際にコードを書くときにも混乱せずに済むでしょう。
- varは関数スコープ、再宣言が可能であり、モダンな開発では非推奨になりつつある
- letはブロックスコープ、再宣言が禁止されているため、予期せぬエラーを防ぎやすい
- 既存コードを保守する場合はvarとの混在があり得るが、新規コードでは原則let(またはconst)を使用
JavaScriptは書き方が多様なので、最初のうちは戸惑うかもしれません。
しかし、一歩一歩仕組みを理解していけば、思い通りに変数を扱えるようになります。
皆さんが次にJavaScriptのコードを書くときは、「この変数はブロック内だけで使いたいのか、それとも関数全体で使いたいのか」 といった設計を考えながら、ぜひletの活用を試してみてください。
そうした意識を持つことが、より読みやすく、意図せぬバグの少ないコードを生み出す一歩となるはずです。