ES6からconst, letが登場しvarは使われなくなりました。
- var, const, letの違いは何か?
- なぜvarは使わないのか?
について説明していきます。
var, const, letの違いは何か?
以下の表に違いをまとめました。
再宣言 | 再代入 | ブロックレベルのスコープ | 変数の巻き上げ | |
---|---|---|---|---|
var | できる | できる | 存在しない | 起こる |
let | できない | できる | 存在する | 起こるが、、 |
const | できない | できない | 存在する | - |
それぞれについて説明していきます!
再宣言
var varVariable; var varVariable; let letVariable; let letVariable; // Uncaught SyntaxError: Identifier 'letVariable' has already been declared
上記のコードでは変数をそれぞれ2回宣言しています。
var varVariable;
は2回目でもエラーは起こりませんが、
let letVariable;
は2回目ではSyntaxErrorが発生しています。
letでは再宣言はできないのです。
また、constの場合、
const constVariable; // Uncaught SyntaxError: Missing initializer in const declaration
そもそも、constで変数を宣言する場合は、値を指定しなければいけません。
それもそうです、後述しますが、constは再代入できない(値を変更できない)ので宣言だけでは使い所がないのです。
再代入
var varVariable = 'var'; varVariable = 'var2'; let letVariable = 'let'; letVariable = 'let2'; const constVariable = 'const'; constVariable = 'const2'; // Uncaught TypeError: Assignment to constant variable.
それぞれ、一度宣言しその後、再代入(値を変更)しています。
var, letでは再代入はできますが、constではできません。 constは他の言語でいう定数のようなものなのです。
ブロックレベルのスコープ
ブロックとは{}
で囲まれたコードのことです。
スコープとは、変数の有効範囲のことで、プログラムのどの場所から参照できるかを決める概念のことです。
つまり、ブロックレベルのスコープとは{}
で囲まれた範囲でのみ変数を参照できるということです。
if (true) { var varVariable = 'var'; } console.log(varVariable); // var if (true) { let letVariable = 'let'; } console.log(letVariable); // Uncaught ReferenceError: letVariable is not defined if (true) { const constVariable = 'const'; } console.log(constVariable); // Uncaught ReferenceError: constVariable is not defined
どれもif節の条件はtrue
なので必ず、ブロックの中を通りますが、var以外は未定義と言われます。
つまり、let, constでの変数は参照できる範囲が限定されているのです。
変数の巻き上げ
変数の巻き上げとは、スコープ内で宣言された変数は、実際に宣言された場所に関係なく、 スコープの先頭で宣言されたことになる というものです。
よくわからないと思うので、実際にコードを見てみましょう。
console.log(varVariable); // undefined var varVariable = 0; console.log(letVariable); // Uncaught ReferenceError: Cannot access 'letVariable' before initialization let letVariable = 0; console.log(constVariable); // Uncaught ReferenceError: Cannot access 'constVariable' before initialization const constVariable = 0;
どれもconsole.log()
を実行した時点では、変数が宣言されていません。
なので、letVariable
,constVariable
はエラーが発生します。
しかし、varVariable
の場合は、undefined
になっています。
何故でしょうか?
これは変数の巻き上げ
が起きるために以下のコードと同じ処理になっているからなのです。
var varVariable; // スコープの先頭で宣言 console.log(varVariable); // undefined var varVariable = 0;
このように、var varVariable;
がスコープの先頭で宣言されていることになるのです。
では、letやconstは変数の巻き上げは起こらないのでしょうか?
そうではありません。実は、 巻き上げを行うが参照することは出来ない のです。
以下のコードを見てください。
let variable = 'This is global.'; var myFunc = function(){ console.log(variable); }; var myFunc2 = function(){ console.log(variable); var variable = 'This is local.'; console.log(variable); }; var myFunc3 = function(){ console.log(variable); let variable = 'This is local.'; console.log(variable); }; myFunc(); // This is global. myFunc2(); // undefined // This is local. myFunc3(); // ReferenceError: variable is not defined
myFunc()ではスコープチェーンが行われます。 ※スコープチェーンとは、JavaScriptで関数内で宣言された変数は、その関数内でしか見ることが出来ない。 関数内に変数が見つからなかった場合、外側のスコープに変数を探しにいく仕組みのことです。
myFunc2()ではvarによる変数の巻き上げが行われています。
問題はmyFunc3()ですが、変数の巻き上げが行われなかった場合は、This is global.
が表示されすはずです。
ですが、実際には参照エラーになっています。
つまり、巻き上げ自体は起こっていますが、それを参照しようとするとエラーになっているのです。
ES6では、letは変数をブロックの先頭へ引き上げます。
しかし、その変数を宣言より前で参照することは、ReferenceErrorを引き起こします。
ブロックの始めから変数宣言が実行されるまで、変数は "temporal dead zone" の中にいるのです。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/let
varを使わない理由
改めてvar, let, constの違いを表にまとめるとこのようになります。
再宣言 | 再代入 | ブロックレベルのスコープ | 変数の巻き上げ | |
---|---|---|---|---|
var | できる | できる | 存在しない | 起こる |
let | できない | できる | 存在する | 起こるが、参照エラー |
const | できない | できない | 存在する | - |
では、なぜvarを使わないのでしょうか。
それは影響範囲を狭くするため!です!
varはlet, constと違いブロックレベルのスコープが存在しません。
一般的なプログラミングのルールでは「スコープはできる限り限定すべき」というものがあります。 変更される可能性が狭い方が修正をしやすいのです。
例えば、1000行と10行のコードそれぞれからバグの原因を見つけるのではどちらが楽でしょうか?
間違いなく後者だと思います。
このような理由から、varよりもlet, constを使うべきなのです。
(↓以前にオブジェクト指向について書いた記事で、グローバル変数の欠点について書いてあります。)
オブジェクト指向でなぜつくるのか?いや、、そもそもオブジェクト指向って? - バグですか?仕様ですか?筋肉です💪
let, constの使い分けは中身の値が変わる可能性があるかどうかで使い分けましょう。
2021/05/03追記: constで定義したオブジェクト、配列はプロパティの変更が可能
const object1 = { name: 'hogehoge', age: 20 } console.log(object1); object1.age = 21 console.log(object1); // {name: "hogehoge", age: 20} // {name: "hogehoge", age: 21}
const array = ['dog', 'cat'] console.log(array); array[0] = 'bird'; array.push('monkey'); console.log(array); // (2) ["dog", "cat"] // (3) ["bird", "cat", "monkey"]
まとめ
再宣言 | 再代入 | ブロックレベルのスコープ | 変数の巻き上げ | |
---|---|---|---|---|
var | できる | できる | 存在しない | 起こる |
let | できない | できる | 存在する | 起こるが、参照エラー |
const | できない | できない | 存在する | - |
スコープを限定するために、varではなくlet, constを使うべき。
let, constの使い分けは中身の値が変わる可能性があるかどうかで使い分けをする。