PICK UP
お知らせ
JavaScript
プログラミング
【JavaScript】var / let / constのベストな使い方

こんにちは、かずん(@kazoonLab)です。
前回の記事では、「JavaScriptのスコープ」についてお話ししました。
今回は、スコープと密接にかかわる、「var / let / const」についてです!

この記事を読むと次のことがわかるよ!
- var / let / constそれぞれの使い方
- かずん流 宣言のベストなやり方
はじめに
そもそも変数とは何か、という話に関しては今回は触れません。今回は、「var / let / const」それぞれの振る舞いが、どのように異なるかを見ていきたいと思います。
変数の基礎的な部分が分からなければ、このリンクが参考になるかと思います。
それぞれの使い方
varとは
varは、古くから変数宣言に用いられてきました。
varは、以下の特徴を持ちます。
- 初期化不要
- 関数スコープである
- 巻き上げが発生する
- 再定義できる
- 再代入できる
スコープについては以下の記事にて触れていますので、そちらをご覧ください。

JavaScriptのスコープまとめ!【global・functional・bl…
JavaScriptの「スコープ」について はじめまして、かずん(@kazoonLab)と申します。 私の担当する記事で…
それでは、例を見てみましょう。
// グローバルな変数を定義
var global;(1)
global = 'global';
function globalFunc () {
// 関数内でローカルな変数を定義
var local = 'local';(2)
console.log(global); // --> global
console.log(local); // --> local
};
console.log(global); // --> global
console.log(local); // --> (Error)(3)
// グローバル変数に再代入する
global = 'global2';
console.log(global); // --> global2
// グローバル変数を再定義する
var global = 'global3';
console.log(global); // --> global3(4)
// 巻き上げを確認する(5)
console.log(newGlobal); // --> undefined
var newGlobal = 'newGlobal';
console.log(newGlobal); // --> newGlobal;
変数の初期化とは、変数宣言と同時に値を代入する行為を指します(上記②のように)
(1)では変数宣言のみ行い、値の代入はしていません。この場合、変数globalはデフォルト値undefinedで初期化されます。
関数globalFunc()内で変数localが宣言されている(2)ため、この関数の外からは変数localは参照できません。(3)
(4)のように、varで宣言された変数は再定義できます(できてしまいます)。変数を多重に宣言することは、意図しないコードの挙動(バグ)の原因となり得ます。
また、varには「巻き上げ」という仕様があります。これは、「あるスコープの中で宣言された変数は、スコープのどの位置で宣言されようとも、スコープの戦闘で宣言されたことになる」というものです。
(5)以下の挙動は、以下のコードと同じになります。
var newGlobal;
console.log(newGlobal); // --> undefined(6)
var newGlobal = 'newGlobal';
console.log(newGlobal); // --> newGlobal;
(6)でundefinedな値を参照していますがエラーになっていません。これも、意図しない挙動といえるでしょう。
では、次にletを見ていきましょう。
letとは
letは、varの不安定な箇所を補うべく誕生しました。
letは、以下の特徴を持ちます。
- 初期化不要
- 関数スコープである
- 巻き上げが発生する
- 再定義できる
- 再代入できる
例を見てみましょう。
// 初期化せず宣言
let global;(1)
// 代入前の変数呼び出しはエラーになる
console.log(global); // --> (error)(2)
global = 'global';(3)
// 初期化して宣言
let global2 = 'global2';
console.log(global2); // --> global2
global = 'global';
if (true) {
// ブロック(if文)の中でローカル変数を宣言
let local = 'local'
console.log(local); // --> local
}
// ブロックの外からは、ローカル変数は参照できない(4)
console.log(local); // --> (error);
let global3 = 'global3';
// 再定義はNG
let global3 = 'global4'; // --> (error)
console.log(global3);
let global4 = 'global4';
// 再代入はOK
global4 = 'global5';
console.log(global4); // --> global5
varと同様、初期化せず宣言した変数はundefinedで初期化されます(1)。ただし、(3)で初期化する前に変数の呼び出しをするとエラーになります(2)。ここがletとvarで異なるポイントです。
厳密にいえば、巻き上げは発生しているのですが、代入前の参照はエラーになるという仕様となっています。
(4)のように、ブロック内で宣言した変数は外から参照できません。このため、ブロック外での意図しない更新を防ぐことができます。
最後に、最も厳密な宣言「const」を見ていきましょう。
constとは
constは、letよりも厳密な変数宣言を行うべく誕生しました。
constは、以下の特徴を持ちます。
・初期化が必要
・ブロックスコープである
・巻き上げが発生しない
・再定義できない
・再代入できない(例外有り)
例を見てみましょう。
// 初期化しない変数宣言はエラー
const global; // --> (error)
const global = 'global';
// 再定義はエラー
const global = 'global2'; // --> (error)
// 再代入もエラー
global = 'global3'; // --> (error)
const global = 'global';
if (true) {
const local = 'local';
console.log(local); // --> local
}
console.log(local); // --> (error)
constで宣言される変数は、宣言と同時にに初期化しなければならず、また再代入・再定義できません。
またブロックスコープであるため、ブロック外からの参照もできません。巻き上げについても、宣言と
一見すると不便なように見えますが、実はとてもありがたい存在なのです。その理由を考えてみましょう。
ある変数がconstで宣言されていて、かつソースコードにエラーが発生していない場合、
「この変数は絶対にほかの場所では使われていない。また、この変数は常に最初の値のままである」
と考えることができるでしょう。つまり、ソースコードを書く人は、意図しない参照を防ぐ(しようとするとエラー)ことができます。
一方。ソースコードを読む人は、ブロック内のconst変数は常に変わらない(*)と信じて読むことができます。
まとめると、constを利用することで、書き手・読み手双方の心理的負担、また費やす時間を削減することができるのです。
(*)プリミティブな値(数字、文字列、…)は再代入できませんが、オブジェクトや配列の中の値の更新はできてしまいます。
const obj = { key: 'value' };
// 中の値の更新はエラーにならない
obj.key = 'value2'; // --> (ok, { key: 'value2' })
// オブジェクトそのものの更新はエラーになる
obj = { key: 'value2'} // --> (error)
// 配列も同じ
const arr = [0, 1, 2];
arr[0] = 1; // --> (ok, [1, 1, 2])
arr = [3, 4, 5]; // --> (error)
まとめ
ここまで、var, let, constそれぞれの特徴について見てきました。
あくまで私の意見ですが、コーディング時にきちんとエラーを出してほしい、また読むときになるべく苦にならないように、
「原則宣言はconstで行い、再代入の必要があればletを使う」
のが良いのではないかと思います。varには別れを告げてしまいましょう。
この記事を読んでくださった方のソースコードが、よりよくなることを願ってやみません。
最後までご覧いただきありがとうございます。
この記事がいいなっと思ったら読者登録をお願いいたします♪