JS の変数定義では let, const を使うべし!…って何で?
JavaScript は最近色々と進化してきていますよね。
で、最近の JS の書き方としてよく言われているのは、
「変数定義では var ではなくて let, const を使うようにしろ!」
ってやつですね。
(あれ?聞いたことないですか?では、今覚えてください。)
let, const の方が新しい書き方なので、そっちを使うようにしよう…くらいな感じでもいいのですが、
せっかくなので、var と let, const がどのように違うのかを調べてみましょう。
ちなみに、以下では var と let を比較して見ていきます。
let と const は、一度定義した後に再度代入ができるかどうかの違いくらいなので、
以下では let の性質 = const の性質と思って読んでください。
var と let の違い
再定義ができるかどうか
一つ目ですが、これが最も重要な違いです。
こんな感じで…
var var_value = ’var’ let let_value = ’let’; // 再定義! var var_value = ’var’ let let_value = ’let’;
一度定義した変数を、再度 var, let で定義すると
SyntaxError: Identifier ’let_value’ has already been declared
var の方は何のエラーも無く、新しい値が代入されますが、
let では再度定義した時点で怒られます。
let では、ある変数名を上の方で使っていることを忘れて、
間違って再度定義するミスを起こさないように、このような仕様になっているのですね。
ちなみに、当然ですが、定義でなくて再代入であれば問題無くできます。
(const は再代入できないようにする宣言なので、const ではできません)
var var_value = ’var’ let let_value = ’let’; // 再代入 var_value = ’var’ let_value = ’let’;
スコープの違い
二つ目はスコープの違い。
スコープとは「変数が見える範囲」のことですね。
これは深く考えずに、とりあえずサンプルを見た方がいいでしょう。
(function () { { var var_value = ’var’; let let_value = ’let’; const const_value = ’const’; console.log(’-----ブロック内-----’); console.log(’ブロック内の var の型 = ’ + typeof var_value); console.log(’ブロック内の let の型 = ’ + typeof let_value); console.log(’ブロック内の const の型 = ’ + typeof const_value); } console.log(’-----ブロック外-----’); console.log(’ブロック外の var の型 = ’ + typeof var_value); console.log(’ブロック外の let の型 = ’ + typeof let_value); console.log(’ブロック外の const の型 = ’ + typeof const_value); })(); console.log(’-----関数外-----’); console.log(’関数外の var の型 = ’ + typeof var_value); console.log(’関数外の let の型 = ’ + typeof let_value); console.log(’関数外の const の型 = ’ + typeof const_value);
ちなみに、console.log の中で、直接値を出力するのではなくて、typeof を使用しているのは、
未定義の変数から値を取り出そうとするとエラーで処理が止まってしまうからです。
今回は変数に文字列を入れていますので、
変数がきちんと存在していれば string
で、
存在しない変数に対して typeof をすると undefined
になります。
これを実行すると、以下のような結果が出ます。
-----ブロック内----- ブロック内の var の型 = string ブロック内の let の型 = string ブロック内の const の型 = string -----ブロック外----- ブロック外の var の型 = string ブロック外の let の型 = undefined ブロック外の const の型 = undefined -----関数外----- 関数外の var の型 = undefined 関数外の let の型 = undefined 関数外の const の型 = undefined
ここで、ブロックとは中括弧 {} のことですね。
ブロックの中で変数を宣言しています。
で、同じブロック内でそれぞれ変数を参照した結果を見てみると、全て string になっています。
すなわち、上で宣言された変数が見えている状態ですね。
ここまでは普通に想定される動きですし、大丈夫でしょう。
では、ブロック外に出るとどうでしょうか。
var で宣言した変数は依然見えているようですが、
let, const で宣言した変数は undefined。すなわち未定義となっています。
これは、let, const は宣言した変数は、同じブロック内でしか参照できないことを表しています。
このように、変数の見える範囲を「スコープ」というのですね。
ちなみに、このようにブロックでスコープが区切られることを「ブロックスコープ」と言います。
まんまですね。
さて次に、さらにその外の関数を出るとどうでしょうか。
この場合は当然 let, const で宣言した変数は見えていません。
加えて、 var で宣言した変数も見えなくなりましたね。
つまり、var は関数でスコープが区切られる、「ファンクションスコープ」ということです。
※ちなみに、変数はスコープの外に出ると見えなくなりますが、
外で宣言された変数はスコープの中に入っても見ることができます。
つまり、
(function () { let let_value = ’let’; { console.log(’ブロック内の let の型 = ’ + typeof let_value); } })();
これは
ブロック内の let の型 = string
と出力されます。
スコープは「影響範囲が外に広がっていかないようにする」機能だと思うとわかりやすいですね。
グローバル変数の、window オブジェクトへの追加
三つ目はグローバル変数の影響範囲です。
グローバル変数とは、どこからでも参照ができる変数ですね。
先ほどのスコープの話を考えれば、一番外側のスコープ、全てのブロックや関数の外側に書けば良いということです。
さて、そのグローバル変数として、ブラウザが予め用意しているものに、 window
というオブジェクトがあります。
window オブジェクトには、今開いているページの情報やブラウザのサイズの情報など、様々な情報が格納されています。
さて、その window オブジェクトとは全く関係ないところで、
グローバル変数をこちらから定義してみますと…
var var_value = ’var’; let let_value = ’let’; const const_value = ’const’; console.log(’window オブジェクトの var = ’ + window.var_value); console.log(’window オブジェクトの let =’ + window.let_value); console.log(’window オブジェクトの const = ’ + window.const_value);
上を実行した結果がこちら
window オブジェクトの var = var window オブジェクトの let =undefined window オブジェクトの const = undefined
なんと、なぜか var でグローバル変数を宣言した場合は、window オブジェクトから参照できてしまいます!
それだけと言えばそれだけですし、そもそもグローバル変数を使うのが云々という話もありますが、
意外な挙動ではあるのではないでしょうか。
var を使わない理由…それは影響範囲を狭くするため!
さて、ここまでで、 var と let & const との違いを見てきました。
すでにお気づきの方もいらっしゃるかと思いますが、
どれも var ではできていたことが let, const ではできなくなっているのですね。
これは、変数定義に関連するミスを極力減らすため、
コードを書く人が見なければならない範囲を減らす、と言う効果があります。
var を使っていたときには発生していた、思わぬ事故を let, const を使用することで防ぐことができるのですね!
皆さんも、JS での変数定義は let, const を使う!と覚えておいてくださいね。