はじめに
JavaScriptの変数の宣言には const ・ let ・ var の3つの方法があります。
let や var は基本的には使わず、 const を使いましょう、とよく言われていますが、じゃあどんな時に let ・ var を使うものなの?と疑問に思っていました。
この記事では、3つの比較や、それぞれの使いどころ・特徴についてまとめました。
比較
| 宣言 | いつ生まれた? | スコープ | 再代入 | 再宣言 | 使いどころ |
|---|---|---|---|---|---|
const |
ES6 | ブロック | ❌ | ❌ | 再代入しない値 |
let |
ES6 | ブロック | ⭕️ | ❌ | あとから更新する値 |
var |
最初期 | 関数/グローバル | ⭕️ | ⭕️ | 原則使わない |
const と let はES6(ES2015)で導入された変数宣言です。
それまでは var でしか変数を宣言する方法がなく、変数のスコープは毎回関数単位で考える必要がありました。
let と const がある現在は、if や for などのブロック単位でスコープを狭くすることができます。
スコープとは、変数を参照できる範囲のことです。
ブロックスコープは変数を宣言したブロック(波括弧 {} の中)内のみ、関数スコープは変数を宣言した関数内のみで参照可能で、グローバルスコープはプログラムのどこからでも参照できます。
そしてすでに記載していますが、 var は現在は非推奨であり、新規のコードでは基本的に使うことはありません。
理由はあとで詳しく記述しますが、再代入・再宣言が可能で、巻き上げと呼ばれる現象が発生してしまうなどさまざまな問題があるからです。
なぜconstが推奨されているか
一般的に const を使うことが推奨されている理由は、再代入が不可能であるため、「その変数に再代入しない」ことをコード上で示せるからです。
レビュワーや後からコードを読む開発者は、後続のコードで x が変わる可能性を考えながら追わなくて済むということです。
const x = 1;
x = 2; // TypeError: Assignment to constant variable.
一方で、let は再代入が可能であるため、後から値が変わる可能性を考慮しながらコードを読む必要があります。
let x = 1;
x = 2; // エラーは発生せず、再代入できる
constは値そのものを不変にするわけではない
const は変数への再代入を防ぎますが、値そのものを完全に不変にする宣言ではありません。
オブジェクトのプロパティや配列の中身を変更することは可能です。
const user = {
name: "Alice",
};
user.name = "Bob";
console.log(user); // { name: "Bob" }
const numbers = [];
numbers.push(1);
console.log(numbers); // [1]
なお、別のオブジェクトや配列を再代入することはできません。
const user = {
name: "Alice",
};
user = {
name: "Bob",
}; // TypeError: Assignment to constant variable.
オブジェクト自体を不変にしたい場合は、Object.freeze() など別の方法を考える必要があります。
letはいつ使う?
読み手の負荷を下げ、安全なコードを書くためにも変数宣言には基本的に constを使用することが推奨されます。
では let はどのような場面で使われるのでしょうか?
下記のコードのように、値が変わることを前提にした変数の場合は、let を使うと自然です。
let count = 0;
count += 1;
count += 1;
console.log(count); // 2
もちろん下記のようなループのカウンタにも let を使います。
for (let i = 0; i < 3; i++) {
console.log(i);
}
const の際は「この値に再代入しない」ということを示せる一方で、let の場合は「この値はあとで再代入される」ということを示すことができます。
letで書きたい処理をconstに書き直す
とはいえやはり let を使いたくなったときは、本当に再代入が必要かを一度考えるとよさそうです。
let で書ける処理の中には const で書いた方が読みやすくなるかもしれないものもあります。
三項演算子を使う
たとえば、下記のように条件分岐の結果をあとから代入するコードです。
let message;
if (isLoggedIn) {
message = "ログイン済みです";
} else {
message = "ログインしてください";
}
この let は、三項演算子を使うと const に書き換えることができます。
const message = isLoggedIn
? "ログイン済みです"
: "ログインしてください";
関数に切り出す
たとえば下記のようなコードです。
let shippingFee;
if (totalPrice >= 5000) {
shippingFee = 0;
} else if (isMember) {
shippingFee = 300;
} else {
shippingFee = 500;
}
この処理を三項演算子にするとネストされてしまって若干読みにくいですね。
const shippingFee = totalPrice >= 5000
? 0
: isMember
? 300
: 500;
このような場合は関数に切り出すと、 let と三項演算子のネストを避けて書くことができます。
function calculateShippingFee(totalPrice, isMember) {
if (totalPrice >= 5000) {
return 0;
}
if (isMember) {
return 300;
}
return 500;
}
const shippingFee = calculateShippingFee(totalPrice, isMember);
varを新しく書くコードで避ける理由
先述の通り、今から新しく書くコードでは var を書くことは原則避けるべきです。
理由は主に3つあります。
スコープが広すぎる
var は関数スコープ/グローバルスコープで扱われるため、下記のコードのようにブロック内で宣言された変数であってもブロック外で参照することができます。
そのため、意図しない場所で同じ変数を使ってしまう可能性があります。
if (true) {
var name = "Alice";
}
console.log(name); // "Alice"
再宣言できてしまう
var では再宣言が可能です。たとえば下記のコードの場合、同じ count という名前の変数が2度宣言されていますが、あとに宣言された 2 が有効となります。
同じ名前の変数を誤ってもう一度宣言してもエラーにならないため、ミスに気づきにくくなってしまいます。
var count = 1;
var count = 2;
console.log(count); // 2
巻き上げが発生する
「巻き上げ」というのは、スコープの先頭で変数が生成されるという事象のことです。
const / let では宣言前の変数を参照するとReferenceErrorが発生しますが、var では undefined になります。
console.log(name); // undefined
var name = "Alice";
厳密には const / let でも巻き上げは発生しますが、 const / let では変数が宣言されるまで初期化が行われないため値を参照できず、ReferenceErrorとなります。
var では巻き上げが発生した時点で変数の初期化が行われるため、 undefined として値が参照可能な状態になってしまいます。
これらの理由から、 var を使うと意図しない挙動やバグを発生させてしまう可能性があるため、使用を避けるべきとされているのです。
まとめ
varは使わない-
constは再代入・再宣言ともに不可能。「再代入しない」ということを示せる -
letは再代入が可能 - 時と場合によるが、
letを使いたくなったら、constで書ける形にできないか一度考える
お読みいただきありがとうございました。