GASの記事を調べていると変数の定義にvarが使われている場合もあれば、const/letが使われていることもあり
「どちらを使えばいいの?」
と疑問に思ったことはないでしょうか?
// varの定義の例
var name = "takeda"
// const/letの例
const address = "Tokyo"
let age = 20
変数を定義するだけならば、どれも同じように使えます。
では一体どんな観点で使い分けをすればよいのでしょうか?
今回は、varとconst/letどちらを使えばいいの?という疑問を解決していきます。
同じように見えて違う動作
varとconst/letで何が違うのか、実際にプログラム例を見ながら説明します。
varでのサンプルプログラム動作
function varSample() {
if(true) {
var name = "sato"
console.log("ifの中")
console.log(name)
}
console.log("ifの外")
console.log(name)
}
実行結果
ifの中
sato
ifの外
sato
let/constでのサンプルプログラム動作
function letSample() {
if(true) {
let name = "sato"
console.log("ifの中")
console.log(name)
}
console.log("ifの外")
console.log(name)
}
function constSample() {
if(true) {
const name = "sato"
console.log("ifの中")
console.log(name)
}
console.log("ifの外")
console.log(name)
}
実行結果(共通)
ifの中
sato
ifの外
ReferenceError: name is not defined
動作の比較
varの動作結果
ifの中
sato
ifの外
sato
const/letの動作結果
ifの中
sato
ifの外
ReferenceError: name is not defined
varはifの中/外両方で変数nameに”sato”という値が格納されていました。
一方const/letではifの外では、変数nameが定義されていないとエラーが出てしまいました。
同じプログラムのように見えますが、なぜ動作が違うのでしょうか?
それは、varとconst/letでそれぞれが持つスコープという概念が違い、影響を及ぼす範囲が異なるからです。
スコープの違い
const/letのスコープ
先に分かりやすいconst/letのスコープから解説します。
const/letで定義された変数はブロックスコープを持ち {} で区切られた範囲で変数が参照できます。
サンプルプログラムは、function(緑色の範囲)、if(赤い範囲)の2つの {} で区切られた範囲があることがわかると思います。
変数がブロックスコープを持つ場合は、同一のブロック内は変数の参照ができますが、ブロック外から中の変数は参照ができません。
サンプルプログラムでは、ifのブロック中で定義されたname変数は同じifのブロック内からは参照できますが、外側のfunctionのブロックからは参照できずにエラーが出力されました。
varのスコープ
一方、varの場合は同一のfunctionのブロックの内側であれば、ブロックの外→中の参照ができます。
サンプルプログラムでは、ifのブロック中での呼び出しはconst/letと同じ動きでした。
外側のfunctionのブロックからは、動きが異なり変数nameが参照できました。
これがvarとconst/letで定義された変数の違いであり、動作が異なる理由です。
可能な限りconst/letを使いましょう
varとconst/letで定義された変数はそれぞれスコープが違うということは分かりました。
では、本題に戻って・・・結局、varとconst/letどちらを使えばいいのでしょうか?
私は可能な限りconst/letを使うべきだと思います。
今回のサンプルプログラムは短くシンプルな内容でしたが、これが複雑で長いプログラムになった場合にブロックの外から内側の変数にアクセスできないというシンプルなルールは非常に役に立ちます。
- const/letで定義された変数は、ブロック内でのみ変更され参照できる
- varで定義された変数は、ブロックの外からも変更され参照できる
こう並べるとvarの方がプログラムを書く上では使いやすそうですが、問題となるのはプログラムを書き終わり、しばらくしてプログラムの挙動を変更したくなった時です。
varで定義された変数はブロックの外からも変更、参照される可能性があるので関数全体を調べる必要があります。
対して、const/letは定義されたブロックの内側だけを調べるだけで済みます。
今後のプログラムの変更しやすさを考えるならば、 可能な限りconst/letを使うべきだと思います。
番外:constとletは何が違うの?
毎回const/letと並べて書いていましたが、この二つは何が違うのでしょうか?
これも実際にプログラムの動作を見ながら説明します。
letでのサンプルプログラム動作
function letSample() {
let name = "sato"
console.log(name)
name = "suzuki"
console.log(name)
}
動作結果
sato
suzuki
constでのサンプルプログラム動作
function constSample() {
const name = "sato"
console.log(name)
name = "suzuki"
console.log(name)
}
動作結果
sato
TypeError: Assignment to constant variable.
動作の比較
letとconstで違いは次の通りでした。
- letで定義した変数はnameをsato->suzukiと書き換えられた
- constで定義した変数はnameを書き換えられなかった
letは、一度値を代入した後でももう一度変数に再代入できます。
対してconstは、一度値を代入した後は代入ができません。
(エラーになります)
このconstのルールもプログラムが長く、複雑になるほど効果を発揮します。
constで定義された変数は変更・参照できるスコープ内で再び代入されることがないことが保証されています。
そのため、プログラムを後から読むときにconstで定義された変数は長い長いプログラムのどの時点においても最初に代入した値が入っていると読むことができます。
途中の過程を読まなくて良いのは、プログラムを読む上で大いに楽ができます。
(このconstで定義された変数は、正しくは不変の定数ではないのですが、これでひと記事書けてしまう内容ので今回は省略します)
この再代入ができないconstのルールを最大限に活用するため、基本的に変数はconstで定義したほうが良いと私は考えます。
まずconstで定義してみて、どうしても途中で再代入が必要な場合のみletを使うようにしましょう。
まとめ
今回は、 varとconst/let どちらを使えばよいのかについて解説しました。
諸説ありますが、私は
- varとconst/letでは、なるべくconst/letを使ったほうが良い
- const/letでは基本的にはconstを使ったほうが良い
と考えています。
そうすることで、プログラムを変更するときにスムーズに影響範囲の調査が済み、プログラム変更にかかる作業時間の削減にもつながります。
ぜひ試してみてください。
const/letを使うべきだと書きましたが、既存のプログラムのvarをすべて置き換えて統一するべきでしょうか?
この場合、特に理由がなければそのまま変更せずにしておくことをオススメします。
varを何も考えずにconst/letに置き換えてしまうと、スコープの違いによって思わぬバグやエラーを引き起こす原因になるためです。