varはもう使わない
varは古い変数宣言の方法です。varにはいくつかの問題点がありました。それを解決するために、ES2015でletとconstが導入されました。ここでは、varとその問題点を説明します。新たにコードを書く場合にはvarは使わずにletとconstを使うことを推奨します。
varの変数宣言
varは次のように書くことで変数を宣言できます。
jsvarname = "taro";
jsvarname = "taro";
初期値を省略した変数宣言もできます。その場合の変数値はundefinedです。
jsvarname ;
jsvarname ;
varの問題点
varによる変数宣言には気をつけるべき挙動が何点か存在します。
同名の変数宣言
varの変数宣言では同じ変数名で宣言をした場合にエラーとならずに、後から宣言された変数が有効となります。これは思いがけず既存の変数を書き換えてしまい、意図しない結果を出力する可能性があります。
jsfunctiontest () {varx = 1;varx = 2;console .log (x );}
jsfunctiontest () {varx = 1;varx = 2;console .log (x );}
letとconstでは、同名の変数宣言はエラーになるようになっています。
tsletx = 1;letx = 2;consty = 1;consty = 2;
tsletx = 1;letx = 2;consty = 1;consty = 2;
グローバル変数の上書き
varはグローバル変数として定義されたときに、windowオブジェクトのプロパティとして定義されるため、既存のプロパティを上書きする危険性があります。
たとえば、ブラウザ上でinnerWidth変数をグローバル変数として定義してしまうと、標準APIのwindow.innerWidthが上書きされるため、ブラウザの幅を変更しても常に同じ値が返ってくるようになってしまいます。
jsvarinnerWidth = 10;console .log (window .innerWidth );
jsvarinnerWidth = 10;console .log (window .innerWidth );
letやconstはグローバルなスコープで定義されることはないため、windowオブジェクトのプロパティを不用意に上書きする心配はありません。
tsconstinnerWidth = 10;console .log (window .innerWidth );
tsconstinnerWidth = 10;console .log (window .innerWidth );
📄️ 変数のスコープ
スコープ(scope)とは、変数がどこから参照できるかを定めた変数の有効範囲のことです。JavaScriptには大きく分けてグローバルスコープとローカルスコープの2つがあります。
変数の巻き上げ
JavaScriptで宣言された変数はスコープの先頭で変数が生成されます。これは変数の巻き上げと呼ばれています。varで宣言された変数は、スコープの先頭で生成されてundefinedで値が初期化されます。次の例ではgreeting変数への参照はエラーとならずにundefinedとなります。
tsconsole .log (greeting );vargreeting = "こんにちは";// ↓ 巻き上げの影響で実際はこう実行されるvargreeting ;console .log (greeting );greeting = "こんにちは";
tsconsole .log (greeting );vargreeting = "こんにちは";// ↓ 巻き上げの影響で実際はこう実行されるvargreeting ;console .log (greeting );greeting = "こんにちは";
varでの変数巻き上げでは参照エラーとならないため、意図せずにundefinedの値を参照し予期せぬバグが発生する危険性があります。
letとconstでは、宣言前の変数を参照するとReference Errorが発生します。
tsBlock-scoped variable 'x' used before its declaration.console .log (); x
Variable 'x' is used before being assigned.2448
2454Block-scoped variable 'x' used before its declaration.
Variable 'x' is used before being assigned.letx = 1;Block-scoped variable 'y' used before its declaration.console .log (); y
Variable 'y' is used before being assigned.2448
2454Block-scoped variable 'y' used before its declaration.
Variable 'y' is used before being assigned.consty = 2;
tsBlock-scoped variable 'x' used before its declaration.console .log (); x
Variable 'x' is used before being assigned.2448
2454Block-scoped variable 'x' used before its declaration.
Variable 'x' is used before being assigned.letx = 1;Block-scoped variable 'y' used before its declaration.console .log (); y
Variable 'y' is used before being assigned.2448
2454Block-scoped variable 'y' used before its declaration.
Variable 'y' is used before being assigned.consty = 2;
ただ、ここで注意すべきなのがletとconstの場合でも変数の巻き上げは発生しているという点です。では、なぜReference Errorが発生するのでしょうか?
varは変数の巻き上げが発生したタイミングでundefinedで変数を初期化しているため、値の参照が可能となっていました。それに対してletとconstは変数の巻き上げが発生しても変数が評価されるまで変数は初期化されません。そのため、初期化されていない変数を参照するためReference Errorが発生しているのです。
次の例ではletやconstで変数の巻き上げが発生しないならconsole.log(x)の評価のタイミングで関数の先頭で宣言されているvar x = 1が参照されて1が出力されるはずです。しかし、実際はletで宣言された変数xがブロックスコープ内で初期化されていない状態で生成されるため、未初期化のxを参照してReference Errorが発生します。
tsfunctionoutput () {varx = 1;{Block-scoped variable 'x' used before its declaration.console .log (); x
Variable 'x' is used before being assigned.2448
2454Block-scoped variable 'x' used before its declaration.
Variable 'x' is used before being assigned.letx = 2;}}output ();
tsfunctionoutput () {varx = 1;{Block-scoped variable 'x' used before its declaration.console .log (); x
Variable 'x' is used before being assigned.2448
2454Block-scoped variable 'x' used before its declaration.
Variable 'x' is used before being assigned.letx = 2;}}output ();
スコープ
JavaScript ではvarで宣言された変数のスコープは関数となるため、{}の中で変数宣言をしても最初に定義した変数xは上書きされます。
tsfunctionvarx = 1;if (true) {varx = 2;console .log (x );}console .log (x );}
tsfunctionvarx = 1;if (true) {varx = 2;console .log (x );}console .log (x );}
letとconstのスコープはブロックスコープです。次の例はvarでは変数xが上書きされていましたが、ここではブロックスコープ内で異なる変数として別々に定義されています。
tsfunctionconstx = 1;if (true) {constx = 2;console .log (x );}console .log (x );}
tsfunctionconstx = 1;if (true) {constx = 2;console .log (x );}console .log (x );}