JavaScriptを少しでも触ったことがある方は、「var」の変数宣言に慣れ親しんで来たと思います。ただ今はES6(ECMAScript2015)以降から追加された、「let」「const」という変数宣言を使う事が主流となっています。
まだvar宣言のみでプログラミングをしている方向けに、この2つの宣言の意味と使い方をご紹介したいと思います。
「let」「const」はブラウザにどこまで対応しているのか?
constやletの宣言は何となく見たことがあるが、今のブラウザでどこまで動くか良くわからないので後回しにしていた方も多いのではないかと思います。
実際にIEが幅を利かせていた時代は使いづらさもありましたが、今のcharome、firefox、safari、Edgeなど主要なブラウザはlet/constに対応しています。クライアント事情でIEが動作保証要件に入っていないのであれば、気にせず使う事ができます。
逆にIEに気を使わないと行けない状況なのであれば「var」をそのまま使う事をお勧めします。IEでもバージョンによっては使えなくも無いですが、少し挙動が違ったりと気を遣う必要があります。この記事ではその部分に関しては割愛させて貰います。
var以外の変数宣言を使う理由
JavaScriptは良くも悪くも緩くプログラミングできるのが特徴でした。ただ、どんどん用途が広がり、その緩さがバグやエラーなどの温床にもなってきたという経緯があります。
その為、プログラマーからはphpなどの他のプログラミングのような厳格な記述が求められるようになり、その要求に徐々に応える形でJavaScriptは発展してきました。
その流れの中でより厳格に変数を扱えるように変数定義として用意されたのがletとconstです。
var/let/constの違いは、再宣言、再代入、スコープ、巻き上げという4つの観点での違いで特徴を理解できます。
次の項からvarとlet/constの違いを紹介していきますので、使い分ける意義を感じて頂ければと思います。
それぞれの変数宣言の違い1:再宣言
あまり聞きなれないかもしれませんが、再宣言とは同じ変数名を何度も宣言し直すことが出来るかどうかを意味します。
varは何度も再宣言ができますが、let/constはエラーとなります。実際に下記コード例を見て下さい。
- var a = '100';
- var a = '200';
- console.log(a); //「200」が表示される
- let b = '100';
- let b = '200'; //エラーとなる
- const c = '100';
- const c = '200'; //エラーとなる
varは再宣言可能なのでエラーとならず「200」が表示されますが、letとconstは再宣言を認めていないのでエラーとなります。
再宣言の動きは何となく理解が出来たかと思いますが、再宣言ができるvarの方が便利そうに感じるかもしれません。
簡単なプログラムなら良いのですが、プログラムが長くなると気づかずに意図せず同じ名前を使ってしまい、変数の中身を上書きしてまった為にエラーとなるケースが発生します。
const/letを使うとそういった予期せぬエラーを防げますので、自身のスキルを上げる意味でもletやconstで慣れていくことをオススメします。
また下記のようなvarの後に、同じ変数名でlet/constを使った宣言をした場合もエラーとなります。
- var a = '100';
- let a = '200'; //エラーとなる
それぞれの変数宣言の違い2:再代入
再代入とは、変数の値を上書きできるかどうかです。再宣言と似ているように見えますが、意味は全く違うのでご注意ください。
varとletと再代入が出来きますが、constは再代入が出来ません。
- var a = '100';
- a = '200'; //aの値は「200」
- let b = '100';
- b = '200'; //bの値は「200」
- const c = '100';
- c = '200'; //エラーとなる
このようにvarやletは変数宣言後に値の変更が可能ですが、constはエラーとなります。
再宣言はそれぞれの2行目にvar/let/constがついた変数宣言でしたが、こちらは再代入なので値を更新しようとしている点に注目してください。
変数のループ処理時や、可変要素の一時的な保持にはvar/letを用います。
一方で値が変更されると困るような値は、const宣言を使います。例えばスマホの表示切替を判断する横幅サイズといった変更されると困るような値をconstで宣言する事で、変数の中身を担保します。
ただconstは再代入できないだけで、const変数の中身が配列などのオブジェクトの場合はプロパティの変更が出来ます。
下記に変更できる例を表示しますが、配列などをconstで宣言しても変更される可能性がある事を頭の片隅に置いておく必要があります。
- const _array = ['a', 'b', 'c'];
- //これはオブジェクトの上書きとなるのでエラーとなる
- _array = ['A', 'B', 'C'];
- //要素の値の変更は可能
- _array[0] = 'A';
- //配列へのpushは可能
- _array.push('D');
それぞれの変数宣言の違い3:スコープの違い
スコープとは変数が参照できる範囲の事で、その範囲から外れると変数は存在しないのと同じ状態になります。
スコープにはブロックスコープ、関数スコープ、グローバルスコープがあります。
それぞれのスコープがどういった範囲まで変数を保持するのか、またver/let/constの挙動がどう違うのかをご紹介します。
ブロックスコープ
ブロックスコープは { } で囲まれた範囲の事で、その中で宣言したlet/const変数はその中でのみ変数が参照できます。
これがvar宣言だと { } の外からも参照されます。
- {
- var a = '100';
- let b = '200';
- const c = '300';
- }
- console.log(a); //「100」が表示される
- console.log(b); // {}の外でbは宣言されていないと同じなのでエラーとなる
- console.log(c); // {}の外でcは宣言されていないと同じなのでエラーとなる
- const c = '400'; //{}の外では変数「c」は宣言されていないのでエラーとならない
- console.log(c); //「400」が表示される
{ } 内の変数はその外では存在しないのと同じなので、4行目で宣言しているconst cを{}の外となる10行目宣言してもエラーとならず宣言が出来ます。
関数スコープ
ブロックスコープの一種に関数スコープというものがあります。関数で定義された中のふるまいを表しブロックスコープではあるのですが、ブロックスコープとは違う挙動をします。
関数の { } で囲まれた中では、ブロックスコープとは違いvar/let/const全てがその中でみの参照となります。
- function test() {
- var a = '100';
- let b = '200';
- const c = '300';
- }
- console.log(a); // {}の外でaは宣言されていないと同じなのでエラーとなる
- console.log(b); // {}の外でbは宣言されていないと同じなのでエラーとなる
- console.log(c); // {}の外でcは宣言されていないと同じなのでエラーとなる
グローバルコープ
グローバルスコープは { } の外で宣言された変数です。var/let/constそれぞれ同じで、どこからでも参照可能となります。
グローバルスコープで宣言された変数をグローバル変数と呼びますが、管理が煩雑になるので極力グローバル変数は使わないプログラミングを心掛ける事をオススメします。
- var a = '100';
- let b = '200';
- const c = '300';
- function test() {
- //変数は { } の外で宣言されていますが参照可能です
- console.log(a); //「var」が表示される
- console.log(b); //「let」が表示される
- console.log(c); //「const」が表示される
- }
- test();
それぞれの変数宣言の違い4:変数の巻き上げ
var宣言にはのみ、関数内でvar宣言をした場合は、すべてその関数の先頭で宣言されたものとみなされ初期化が行われます。この動きを変数の巻き上げと呼びます。
少しイメージがしづらいとも思いますので、具体的な下記のコードを見て下さい。
- var a = 'var1';
- function test() {
- console.log(a); //undefinedとなる
- var a = 'var2';
- console.log(a); //「var2」が表示される
- }
- test();
test関数の最初の「a」変数の中身は、グローバル宣言で代入した「var1」が表示されそうですが、実際は「undefined」となります。
これが先ほど記載した変数の巻き上げという挙動で、test関数内で「var a = 'var2'」があるので、関数内の先頭で「var a;」が実行された挙動が走り、最初のa変数の中身がudefinedとなります。
変数の巻き上げ自体はJavaScript特有の挙動でエラーでは無いのですが、独特な概念の為に、var宣言を使う事によって起こりえるバグの発生原因となります。
var宣言には変数の巻き上げが発生しますが、let/constでは発生しない事象です。
「var」「let」「const」の違いをまとめると
あらためてvar/let/constの違いをここにまとめます。各変数宣言の違いの理解の為の参考にしてください。
var | let | const | |
---|---|---|---|
再宣言 | ○ | × | × |
再代入 | ○ | ○ | × |
ブロックスコープ | どこからでも参照可 | { } 内でのみ参照可 | |
関数スコープ | 関数内でのみ参照可 | ||
グローバルスコープ | どこからでも参照可 | ||
変数の巻き上げ | ○ | × | × |
最後に
これがvar/let/constの違いとなりますが、最初は少しややこしく感じるかもしれません。ただvarを使う事は何となく良く無さそうだというのは理解いただけたかと思います。
本来はconstを使いそれで挙動がおかしければletを使う事が推奨されていますが、最初はvarをletに置き換えるだけでも予期せぬバグを未然に防ぐ手立ての一助にはなると思います。
そういったやりやすい所から始め、基本的にはvar宣言が無くなるコードにしていく事が大事かと思います。