varはもう使わない
var
は古い変数宣言の方法です。var
にはいくつかの問題点がありました。それを解決するために、ES2015でlet
とconst
が導入されました。ここでは、var
とその問題点を説明します。新たにコードを書く場合にはvar
は使わずにlet
とconst
を使うことを推奨します。
varの変数宣言
var
は次のように書くことで変数を宣言できます。
js
varname = "taro";
js
varname = "taro";
初期値を省略した変数宣言もできます。その場合の変数値はundefined
です。
js
varname ;
js
varname ;
varの問題点
var
による変数宣言には気をつけるべき挙動が何点か存在します。
同名の変数宣言
var
の変数宣言では同じ変数名で宣言をした場合にエラーとならずに、後から宣言された変数が有効となります。これは思いがけず既存の変数を書き換えてしまい、意図しない結果を出力する可能性があります。
js
functiontest () {varx = 1;varx = 2;console .log (x );}
js
functiontest () {varx = 1;varx = 2;console .log (x );}
let
とconst
では、同名の変数宣言はエラーになるようになっています。
ts
letx = 1;letx = 2;consty = 1;consty = 2;
ts
letx = 1;letx = 2;consty = 1;consty = 2;
グローバル変数の上書き
var
はグローバル変数として定義されたときに、window
オブジェクトのプロパティとして定義されるため、既存のプロパティを上書きする危険性があります。
たとえば、ブラウザ上でinnerWidth
変数をグローバル変数として定義してしまうと、標準APIのwindow.innerWidth
が上書きされるため、ブラウザの幅を変更しても常に同じ値が返ってくるようになってしまいます。
js
varinnerWidth = 10;console .log (window .innerWidth );
js
varinnerWidth = 10;console .log (window .innerWidth );
let
やconst
はグローバルなスコープで定義されることはないため、window
オブジェクトのプロパティを不用意に上書きする心配はありません。
ts
constinnerWidth = 10;console .log (window .innerWidth );
ts
constinnerWidth = 10;console .log (window .innerWidth );
📄️ 変数のスコープ
スコープ(scope)とは、変数がどこから参照できるかを定めた変数の有効範囲のことです。JavaScriptには大きく分けてグローバルスコープとローカルスコープの2つがあります。
変数の巻き上げ
JavaScriptで宣言された変数はスコープの先頭で変数が生成されます。これは変数の巻き上げと呼ばれています。var
で宣言された変数は、スコープの先頭で生成されてundefined
で値が初期化されます。次の例ではgreeting
変数への参照はエラーとならずにundefined
となります。
ts
console .log (greeting );vargreeting = "こんにちは";// ↓ 巻き上げの影響で実際はこう実行されるvargreeting ;console .log (greeting );greeting = "こんにちは";
ts
console .log (greeting );vargreeting = "こんにちは";// ↓ 巻き上げの影響で実際はこう実行されるvargreeting ;console .log (greeting );greeting = "こんにちは";
var
での変数巻き上げでは参照エラーとならないため、意図せずにundefined
の値を参照し予期せぬバグが発生する危険性があります。
let
とconst
では、宣言前の変数を参照するとReference Error
が発生します。
ts
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 = 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;
ts
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 = 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が発生します。
ts
functionoutput () {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 ();
ts
functionoutput () {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
は上書きされます。
ts
functionvarx = 1;if (true) {varx = 2;console .log (x );}console .log (x );}
ts
functionvarx = 1;if (true) {varx = 2;console .log (x );}console .log (x );}
let
とconst
のスコープはブロックスコープです。次の例はvar
では変数x
が上書きされていましたが、ここではブロックスコープ内で異なる変数として別々に定義されています。
ts
functionconstx = 1;if (true) {constx = 2;console .log (x );}console .log (x );}
ts
functionconstx = 1;if (true) {constx = 2;console .log (x );}console .log (x );}