let と const を使い分ける
このレッスンで分かること
constは再代入不可、letは再代入可能のブロックスコープ束縛です- 迷ったら
const、書き換えが必要ならletを使います- 最小例は
const taxRate = 0.1; let total = 0;
let と const を使い分ける とは
再代入のあり/なしで let と const を使い分け、定数で関数を組む。本レッスンでは、let と const を使い分ける の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
let と const を使い分ける
JavaScript では値に名前を付けるときに let と const の 2 種類を使います。var という古いキーワードもありますが、現代のコードでは原則使いません。本レッスンでは let と const の違いを押さえ、まずは「迷ったら const」という基本姿勢を体に染み込ませます。const は 再代入不可 な束縛、let は 再代入可能 な束縛です。どちらもブロックスコープを持ち、関数の中だけ・if 文の中だけといった狭い範囲で安全に変数を扱えます。
ヒント
constは値そのものを凍結するわけではなく、変数名と値の結び付きを固定するだけです。
const の基本
const は宣言と同時に値を入れる必要があります。たとえば const PI = 3.14; のように書きます。後から PI = 3 のように書き換えようとすると TypeError になります。チームで開発するとき、const で宣言されている変数は「以後この束縛は変わらない」という宣言になり、読み手の認知負荷を大きく下げます。const を見たら「ここから先で再代入はないはずだ」と安心して読めるわけです。
JavaScript
const taxRate = 0.1;
const greet = (name) => `こんにちは ${name} さん`;
console.log(greet("なお"));上の例では taxRate と greet を const で宣言しました。値が数値でも関数でも、再代入しないなら const で問題ありません。const は型を縛らないので、const items = []; としても配列の中身 push は可能です。これは束縛と値の中身の違いです。
let を使うべき場面
一方で、ループのカウンタや状態を持つ変数のように、後から値を書き換えたい場面では let を使います。たとえば let count = 0; とした上で count = count + 1 のように増やしていけます。let を見た瞬間、読み手は「この変数は後で書き換わるかもしれない」と心の準備ができます。
JavaScript
let total = 0;
for (let i = 1; i <= 3; i = i + 1) {
total = total + i;
}
console.log(total);上のループでは i を let で宣言しています。i はループのたびに値が変わるので const では不適切です。total も同様に毎回足し算で更新します。逆に、ループ内で参照だけして変更しないなら const を使うと、誤った代入をエディタや lint がブロックしてくれます。
| キーワード | 再代入 | スコープ | 用途 |
|---|---|---|---|
const | 不可 | ブロック | 既定。値を書き換えないとき |
let | 可能 | ブロック | ループカウンタ・状態 |
var | 可能 | 関数 | 非推奨。互換のためのみ存在 |
スコープの違いを図で確認
let と const はどちらも ブロックスコープ を持ちます。{ ... } で囲まれた範囲を抜けると、変数は存在しなくなります。一方、古い var は関数スコープなので if の中で宣言しても外から見えてしまい、思わぬバグの原因になります。
図の流れを順序リストで書き直すと、次の通りです。
constまたはletでブロック内に変数を宣言する- 同じブロック内では参照できる
- ブロック外で参照すると
ReferenceError - ブロック内で再代入を試みる
letなら成功、constならTypeError
メモ ブロックスコープのおかげで、変数の寿命を最小限に絞れます。
よくある間違い
1 つ目は const arr = []; の配列に push できないと誤解するパターンです。const が固定するのは束縛なので、配列やオブジェクトの中身は変更可能です。arr.push(1) は OK ですが、arr = [1] は NG という違いを意識しましょう。2 つ目は let で宣言しただけで初期化しないままアクセスするケースです。let value; だけだと value は undefined になり、計算で NaN を生み出す原因になります。3 つ目は同じ名前を let で 2 回宣言してしまう書き方です。同じスコープ内で let foo = 1; let foo = 2; と書くと SyntaxError になります。
注意 「再代入しないなら
const、するならlet」を口癖にすると迷いません。
const と let の比較表
書き方の違いを意識するため、Before/After で並べてみます。Before は let ばかりですが、再代入しない変数は const に置き換えるのが After です。これだけで「ここから値は変わらない」というメッセージが読み手に伝わります。
JavaScript
// Before どの変数が変わるのかわかりにくい
let name = "なお";
let age = 30;
let greeting = `${name} さんは ${age} 歳`;
// After 再代入しないので const に揃える
const name2 = "なお";
const age2 = 30;
const greeting2 = `${name2} さんは ${age2} 歳`;読み手が const の塊を見れば「ここは安全」と判断でき、変更箇所を探す手間が減ります。これはコードの可読性を一段引き上げる小さな工夫です。
ここまでの要点
迷ったら const、書き換えるなら let、var は使わない。const でも配列やオブジェクトの中身は変更可能 (束縛だけ固定)。
やってみよう
次の summarize 関数を const だけ を使って実装してください。引数 price (数値) と name (文字列) を受け取り、{ name, total } を返します。total は price に 1.1 を掛けた整数値 (Math.round で四捨五入) です。let や var を使うとレッスンの趣旨に反するので、定数 taxRate と total を const で組み立てる練習をしましょう。
書き上がったら、各テストケースが通るか確認してください。const で書き切れる場面では const を選ぶ習慣を、このレッスンで身に付けましょう。
よくある質問
Q. let と const はどう使い分けますか?
A. 再代入しない値はすべて const、ループカウンタや明示的に書き換える変数だけ let が原則です。const なら誤った再代入を防げて読み手に意図が伝わります。オブジェクトの場合でも参照は固定なので、プロパティ変更を許す代わりに参照差し替えを禁じる目的で使えます。
Q. var はもう使わなくて良いですか?
A. 現代のコードでは原則使いません。var は関数スコープで巻き上げが起きるため意図しないバグの温床になります。ESLint の no-var ルールで強制し、既存コードを触るときに見つけたら let/const へ置き換えるのが安全です。
Q. ブロックスコープと関数スコープは何が違いますか?
A. ブロックスコープ(let/const)は { ... } の内側だけで有効、関数スコープ(var)は関数全体で有効です。ループ内で let を使うと毎周回ごとに新しい変数が作られ、クロージャと組み合わせたときに直感通り動きます(var だと全部最後の値になる)。
次のレッスン
次は typeof で型を調べる で、再代入のあり/なしで let と const を使い分け、定数で関数を組む を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- let と const の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. let と const とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- const を使って taxRate を 0.1 で宣言する
- Math.round で四捨五入した整数を total に入れる
- { name, total } の形のオブジェクトを返す
入出力例
test-cases.txt
summarize(1000, "なお") → {"name":"なお","total":1100}
summarize(1234, "Tom") → {"name":"Tom","total":1357}
summarize(0, "Zero") → {"name":"Zero","total":0}