現在はJavaScriptで変数を宣言するときにletを使用するのが主流ですが、ES6よりも前のバージョンではvarを使用していました。
私がこの業界に入った時点で既にES6になっており、letを使用することを推奨されたため、varを使用したことがありません。
今回、改めて違いを知りたいなと思いそれぞれの違いをまとめてみました。
➀スコープ
letとvarでは、変数の参照できる範囲「スコープ」が異なります。
letを使用する場合、if文の中で変数を設定すると「ブロックスコープ」になります。
そのため、下記の例のようにif文の外で変数を参照するとエラーが発生します。
if(true) {
let x = 2;
console.log(x); // 2
}
console.log(x); // Uncaught ReferenceError: x is not defined
一方、varを使用して変数宣言を行った場合、どれだけ深い入れ子になっていても変数のスコープは関数全体になります。
if(true) {
var x = 2;
console.log(x); // 2
}
console.log(x); // 2
➁グローバル変数
letもvarも関数の外側で変数を宣言した場合、この変数はグローバル変数になります。
しかしこれらは重要な点が異なります。
varを使って宣言したものは、グローバルオブジェクトのプロパティとして実装されます。
グローバルオブジェクトとは
グローバルオブジェクトは、グローバルスコープ上に常時存在するオブジェクトです。
JavaScript では、グローバルオブジェクトが常に定義されています。ウェブブラウザー上でスクリプトがグローバル変数を生成する時、グローバルオブジェクトのメンバーとして作成されます。
つまり、グローバルオブジェクトの関数や変数はどこからでも参照することができるのです。
グローバルオブジェクトはglobalThisで参照できるので、関数の外側でvar x = 2;と記述した場合、globalThis.x = 2;と記述したのと同じになります。
なお、letを使って宣言したグローバル変数は、グローバルオブジェクトのプロパティになりません。
var x = 2;
console.log(globalThis.x); // 2
let y = 2;
console.log(globalThis.y); // undefined
ただし、まったく同じグローバル変数というわけではありません。varを使って宣言したプロパティはdelete演算子をつかって削除できません。
// 通常のグローバル変数
globalThis.globalVar = "hello world";
console.log(globalThis.globalVar); // hello world
delete globalThis.globalVar;
console.log(globalThis.globalVar); // undefined
// varで宣言
var myVar = "hello world";
console.log(globalThis.myVar); // hello world
delete globalThis.myVar;
console.log(globalThis.myVar); // hello world
➂再宣言
一度宣言した変数を、同じ変数名で宣言することを再宣言と言います。
letを使用する場合は再宣言するとエラーが発生しますが、
varの場合は後に宣言した変数が適用されます。
// let
let x = 2;
let x = 10;
console.log(x); // SyntaxError: Identifier 'x' has already been declared
// var
var y = 2;
var y = 10;
console.log(x); // 10
➃ホイスティング(巻き上げ)
そもそもホイスティングとは?
avaScript の巻き上げ (Hoisting) は、インタープリターがコードの実行前に、関数、変数、クラス、インポートの宣言をそのスコープの先頭に移動するように見えるプロセスを指します。
言葉だけでは分かりづらいので実際に例を見てみましょう。
下記はletの例です。
console.log(x); // Cannot access 'x' before initialization
let x = 2;
console.log(x); // 2
letの宣言前に参照しようとするとエラーが発生します。
では、varの場合はどうでしょう。
console.log(x); // undefined
var x = 2;
console.log(x); // 2
エラーではなく、undefinedが出力されています。
varの場合、宣言されるとコード実行前にメモリが確保されます。
あくまでメモリの確保の実で、値は代入されていないのでundefinedとなっています。
おわりに
使い方は同じとは言え、varは予期せぬエラーを誘導する可能性を孕んでいます。
意図しない動作を防ぐためにも、基本的にはletやconstを使用する方がよさそうです。
それでは。