JavaScript(以下JS)で変数を宣言する(作る)場合、varとletとconstの3つの方法があります。JSは宣言をしなくても、値を代入することができますので、何も書かないという方法を加えると厳密には4つになります。
とりあえずなんでもvarで宣言している人も多いのではないかと思います。その状態で「最近はconstだよ」なんて簡単に知識を得て使い始めると、突然動かなくなったりしてしまいます。
もちろん、全部varにしておくのは下手に使い分けるより安全ですが、きちんと違いを理解しておいた方がいいです。
そして、この3つの方法の違いを理解する前知識として、変数と定数、グローバル変数とローカル変数の違いを理解する必要があります。
変数と定数の違い
まず変数と定数の違いを理解することが必要です。
変数とは
変数とは、「データの入れ物」であり、数値や文字列を格納することができます。そして重要なのが、プログラムの途中で中身を入れ替えることができます。変数はvarもしくはletで宣言します。何も書かないと定数ではなく変数の扱いになります。
定数とは
定数とは、途中で中身を入れ替えることのできない値です。常に入れ物と中身がセットで、プログラムの途中で中身を上書きするこができません。定数はconstで宣言します。constで宣言した定数を途中で上書きしようとするとエラーになります。
グローバル変数とローカル変数の違い
作った変数がどこから参照できるのか、という範囲をスコープと言います。スクリプト全体から参照できる範囲をグローバルスコープ、特定の関数の中でのみ参照できる範囲をローカルスコープと言います。
グローバルスコープを持つ変数をグローバルス変数、ローカルスコープを持つ変数をローカル変数といいます。
そしてもう1つ、ブロックスコープという参照範囲があります。ローカルスコープよりもさらに狭い範囲になります。これらを踏まえて解説すると下記のようになります。
グローバル変数とは
グローバルスコープを持つ変数・定数のことで、どの関数からも参照できる値です。グローバル変数を作るには、関数の外で宣言するか、var・let・constのどれも書かずに宣言する方法があります。
ローカル変数とは
ローカルスコープを持つ変数・定数のことで、特定の関数からのみ参照できる値です。ローカル変数を作るには、関数の中で、var・let・constのいずれかを使って宣言します。
ブロック変数とは
ブロックスコープを持つ変数のことで、if文などの限られたブロックでのみ参照できる値です。letを使って宣言します。
var・let・const・無記・の違い
それぞれの宣言方法を表にまとめてみます。
スコープ(参照範囲) | 変数・定数 | |
var | グローバル・ローカル | 変数(同名でも動く) |
let | グローバル・ローカル・ブロック | 変数(同名不可) |
const | グローバル・ローカル | 定数(上書き・同名不可) |
無記 | 強制的にグローバル | 変数 (同名でも動く) |
varは同名の変数を宣言しても上書きとして扱われますが、letは同名の変数を宣言するとエラーになります。
実際の挙動を確認するソース
それぞれの変数の宣言方法の挙動を確認するソースを書いてみました。エラーになる記述はコメントアウトにしてあります。グローバル変数と関数を2つ、ブロックを1つ用意し、それぞれ参照できる範囲や上書きの可否を検証する内容です。コピーしてコメントアウトを付けたり外したりすると解りやすいです。
var test = "Global_var";
const test2 ="Global_const";
function testFunction(){
console.log(test); //結果:Global_var
console.log(test2); //結果:Global_const
if(true){
let test3 = "Block_let";
console.log(test3); //結果:Block_let
}
test4 = "KyoseGlobal";//←関数内でグローバル変数を宣言。
// console.log(test3); //←letで宣言したブロック変数をブロック外では参照できない。
// var test = "Global_var2"; //←グローバル変数として書いた方のtestがundefinedになる。
// test2 = "Global_const2"; //←constで宣言したものを上書きしようとするとエラーになる。
return test; //関数内でtestを上書きすると、それが返される。
}
function test2Function(){
console.log(test);//←別の関数内で上書きされてもグローバルの方のtestが参照される。
console.log(test4);//←別の関数内で宣言した変数でも無記の強制グローバルなので参照できる。
}
console.log(testFunction());
test2Function();
図説と補足
補足として、“変数の巻き上げ”を図の中で解説しています。関数内でのvar宣言は同じ名前のグローバル変数よりも優先されますが、実行(ここではconsole.log)よりも先に書いていないと、値は参照できません。
変数宣言のまとめ
4つの宣言方法の違いは理解できたと思います。では実際にどう使い分けるかですが、これについては、開発の規模に影響すると思います。私の個人的な意見にはなりますが、初心者やWEBデザイナーがデザインに動きを付けるというレベルであれば、
“変数名が重複しないようにvarで宣言する”
これがもっとも安全なのではないかと思います。解説しておいてなんですが。
ここから、関数が複雑になったり、変数の数が膨大になったり、あるいはブラウザの値を参照して色々な関数に使ったりとなってきたときに使い分けていくのが良いかと思います。