原文: https://www.freecodecamp.org/news/var-let-and-const-whats-the-difference/
新しい機能がたくさん ES2015 (ES6) で発表されました。2020 年現在、多くの JavaScript の開発者はその機能に慣れ、そして使い出したと考えられています。
この考えは部分的には正しいかもしれませんが、一部の開発者には、その機能のいくつかはいまだに謎のままかもしれません。
ES6 で発表された機能のひとつに、let
と const
の追加があります。これらは、変数宣言に用いられます。問題となるのは、私たちが使用してきた古き良き var
とどう違うのかということです。もし、この違いをまだ十分に理解されていないならば、この記事が役に立つことでしょう。
この記事では var
、let
、const
のそれぞれのスコープ、使用法、宣言の巻き上げ (hoisting) に関して説明します。これから挙げられる、各宣言の違いに注目しながら読み進めてください。
Var
ES6 の到来以前は、var
宣言が使われていました。しかし、var
で宣言された変数には問題がありました。そのため変数を宣言する、新しい方法が登場する必要がありました。その問題を説明する前に、まずは var
をより理解することから始めていきましょう。
var のスコープ
スコープとは本来、変数が使用できる場所を意味します。var
宣言は、グローバルスコープまたは関数/ローカルスコープです。
関数の外で var
によって変数が宣言される場合、その変数はグローバルスコープです。これは、関数ブロックの外において var
で宣言されたあらゆる変数は、プログラム全体のどこでも使用できることを意味します。
関数の中で var
によって変数が宣言される場合、その変数は関数スコープです。これは、宣言した変数が、その関数の中でのみ使用可能であることを意味します。
さらに理解を深めるため、以下の例をご覧ください。
var greeter = "hey hi";
function newFunction() {
var hello = "hello";
}
ここで、関数の外にある greeter
はグローバルスコープです。一方、hello
は関数スコープです。関数の外から変数 hello
にはアクセスできません。そのため、次のように記述した場合には:
var tester = "hey hi";
function newFunction() {
var hello = "hello";
}
console.log(hello); // error: hello is not defined
関数の外では hello
が使用できないことを示すエラーが発生します。
var で宣言された変数は、再宣言も更新も可能
次のように、同じスコープ内であれば、var
によって宣言された変数は再宣言も更新も可能であり、エラーも発生しません:
var greeter = "hey hi";
var greeter = "say Hello instead";
これはまた、次のようにも記述できます:
var greeter = "hey hi";
greeter = "say Hello instead";
var 宣言の巻き上げ
巻き上げとは、コードを実行する前に、変数や関数の宣言がそのスコープの先頭に移されるという JavaScript の仕組みです。これは、以下のように記述した場合には:
console.log (greeter);
var greeter = "say hello"
次のように解釈されることを意味します:
var greeter;
console.log(greeter); // greeter is undefined
greeter = "say hello"
つまり、var
で宣言された変数は、そのスコープの先頭に巻き上げられ、値は undefined
に初期化されます。
var に伴う問題
var
には弱点があります。次の例を見ながら解説します:
var greeter = "hey hi";
var times = 4;
if (times > 3) {
var greeter = "say Hello instead";
}
console.log(greeter) // "say Hello instead"
ここで、times > 3
は true を返しますので、greeter
は "say Hello instead"
に再定義されます。greeter
が再定義されるということをわかっていれば問題はありませんが、一方で、それ以前に変数 greeter
が既に定義されていることに気づいていない場合は問題です。
気づいていないままコードの他の部分の中で greeter
を使用した場合、得られる結果に驚かされるかもしれません。このことは、コードの中で多くのバグを引き起こしかねません。それを避けるために、let
と const
が必要になります。
Let
let
は現在、変数の宣言に好んで用いられています。それは、var
宣言の改善として提供されているので、当然のことでしょう。また let
は、先ほど取り扱った var
の問題を解決します。これから、なぜそのようなことが可能なのかを見ていきましょう。
let 宣言はブロックスコープ
ブロックとは、{} に囲まれたコードのかたまりです。ブロックは、中括弧の中にあります。中括弧の中にあるすべてのものが、ブロックです。
ブロックの中で、 let
で宣言された変数は、そのブロックの中でのみ使用できます。次の例を見ながら解説します:
let greeting = "say Hi";
let times = 4;
if (times > 3) {
let hello = "say Hello instead";
console.log(hello);// "say Hello instead"
}
console.log(hello) // hello is not defined
ここで、ブロック (変数が宣言された中括弧) の外で hello
を用いようとして、エラーが発生しています。let
で宣言された変数は、ブロックスコープだからです。
let で宣言された変数は、更新はできても、再宣言はできない
var
と同様に、let
で宣言された変数は、同じスコープの中で更新できます。ただし、var
とは違い、let
で宣言された変数は、そのスコープの中での再宣言はできません。以下のコードは動作しますが:
let greeting = "say Hi";
greeting = "say Hello instead";
一方で、次に示したコードは、エラーを返します:
let greeting = "say Hi";
let greeting = "say Hello instead"; // error: Identifier 'greeting' has already been declared
しかし、同じ名前の変数が、別のスコープの中で定義される場合には、エラーは発生しません:
let greeting = "say Hi";
if (true) {
let greeting = "say Hello instead";
console.log(greeting); // "say Hello instead"
}
console.log(greeting); // "say Hi"
どうしてエラーが起こらないのでしょうか?これは、異なるスコープにあるために、両方のインスタンスが異なる変数として扱われるためです。
このため、let
を選択するほうが var
よりも良いとされています。let
を用いる時、変数はそのスコープ内にのみ存在するため、それよりも前に変数名を使用したかどうかについて、悩まなくてもよくなります。
また、let
で宣言された変数は、スコープの中で複数回宣言できないため、前述の var
の問題は起こりません。
let 宣言の巻き上げ
var
と同様に、let
宣言は先頭に巻き上げられます。undefined
で初期化される var
との違いは、let
キーワードは初期化されないということです。そのため、let
で変数をある名前で宣言するより前に、その名前の変数の使用を試みた場合は、Reference Error
が発生します。
Const
const
で宣言された変数は、定数値を保持します。const
宣言は let
宣言とよく似ています。
const 宣言はブロックスコープ
let
宣言と同様に、const
で宣言された変数は、宣言されたブロックの中でのみアクセスできます。
const で宣言された変数は、更新も再宣言もできない
const
で宣言された変数の値は、そのスコープの中では変化しません。更新することも、再宣言もできません。そのため、const
で変数を宣言した場合、次のようには記述できませんし:
const greeting = "say Hi";
greeting = "say Hello instead";// error: Assignment to constant variable.
また、次のようにも記述できません:
const greeting = "say Hi";
const greeting = "say Hello instead";// error: Identifier 'greeting' has already been declared
よって、const
で宣言される変数はすべて、宣言時に初期化する必要があります。
const
で宣言されたオブジェクトについては、この動作はどういうわけか異なります。const
で宣言されたオブジェクトは、更新できないとしても、そのオブジェクトのプロパティーは更新できます。従って、次のようにして const
でオブジェクトを宣言した場合には:
const greeting = {
message: "say Hi",
times: 4
}
次のようには記述できませんが:
greeting = {
words: "Hello",
number: "five"
} // error: Assignment to constant variable.
一方で、次のようには記述できます:
greeting.message = "say Hello instead";
こうすることで、エラーを返さずに greeting.message
の値を更新します。
const 宣言の巻き上げ
let
と同様に、const
宣言は先頭に巻き上げられますが、初期化されません。
それでは、違いを見落とした場合に備えて、以下にまとめておきます:
var
宣言はグローバルスコープまたは関数スコープである一方で、let
宣言とconst
宣言はブロックスコープです。var
で宣言された変数は、そのスコープの中で更新できますし再宣言できますが、let
で宣言された変数は、更新はできても再宣言はできず、const
で宣言された変数は、更新も再宣言もできません。- 各宣言は、すべてそのスコープの先頭まで巻き上げられます。しかし、
var
で宣言された変数が、undefined
で初期化される一方で、let
とconst
で宣言された変数は、初期化されません。 var
とlet
は初期化しなくても宣言できますが、const
は、宣言時に初期化される必要があります。
何かご質問やご要望があれば、ご連絡ください。
お読みいただきありがとうございました :)