[Javascript] var,let,const の違い

当ページのリンクには広告が含まれています。
目次

はじめに

今回はJavascriptの変数定義の話です。

他の参考記事などでよく再代入出来るかできないかの違いしか掲載していないですが、

実際はそれ以外にも違いがあり、

  • javascript のホイスティングされるかどうか
  • ブロックスコープが適用されるかどうか

といった違いもあり、それらの理由からvarが非推奨になっていることを知ったので、備忘のために記事にすることにしました。

結論

ということで、まず結論としては以下のような違いがあるそうです🦈

varlet const
再代入×
ホイスティング××
ブロックスコープ×

よって、他の静的型付言語と同様な実行をさせたいのであれば、let,const のみを使えばよい
varはホイスティングによって定義の位置が呼び出し箇所よりも後に記述しても動いてしまうため、コードの可読性を下げ、バグを生みやすくなるので扱いには注意が必要。

ホイスティングとは?
コンテキスト内で宣言した変数や関数の定義を実行前にメモリーへ配置すること

コンテキストとは?
コンテキスト(Context)という言葉の意味は「前後関係、文脈、状況、環境」といった意味がある。
ここでいう関数が実行される際の文脈や状況のことを関数コンテキストと言ったりする。
また、JS が実行される環境(ブラウザ内で実行される、サーバーで実行される)をグローバルコンテキストという
→ コードでいうと以下の箇所が関数コンテキストといい、また関数コンテキストの外をグローバルコンテキストと考えてよい

グローバルコンテキストではブラウザで実行される場合、window API等が呼び出せる

//外をグローバルコンテキストと考えてよい(実際は少し解釈が違うかもしれない)
function test() {
  //ここの中のことを関数コンテキストという
}

ブロックスコープとは?
{}の中の範囲のことを指す
例としては、if 文や for 文など、関数も使うが js では別途関数スコープと呼ばれるものに区別されるため、同じではないらしい

if (flg) {
  //ここの範囲
}

for (let index = 0; index < array.length; index++) {
  //ここの範囲
}

では、結論の内容を詳しく説明していきます。

var

変数の再代入可能か?

→ 可能

ホイスティングされるか?

→ される

ブロックスコープ

→ 無視され、意図しない挙動になる

変数の場合

再代入

以下のよう再代入をすると値を更新することができる

var sample = 1
//再代入
sample = 2

console.log(sample)

//結果
//2

ホイスティング

ホイスティングされるため、以下のように定義の前に呼び出しを記述し、定義を呼び出しよりも後に記述してもエラーが起きることなく動いてしまう。
また、これは特に注意が必要で、定義していても初期値がundefinedで扱われてそのまま実行される

//定義の前に呼び出しを記述
console.log(sample)

//定義を呼び出しよりも後に記述
var sample = 1

//結果
//undefined

ブロックスコープ

ブロックスコープが無視されるため、以下のような記述でも動作する。

if (true) {
  var sample = 1
}

console.log(sample)

//結果
// 1

関数の場合

関数の場合はvarを使って関数式で定義した場合は上記のようなことは起きず、
代わりにfunction {関数名}といった定義の場合に変数同様に定義の場所関係なく実行が可能。

//定義の前に呼び出しを記述
test()

//定義を呼び出しよりも後に記述
function test() {
  console.log('Hello')
}

//結果
//Hello

以下の場合はTypeError: test is not a functionでエラーとなる

//定義の前に呼び出しを記述
test()

//定義を呼び出しよりも後に記述
var test = () => {
  console.log('Hello')
}

//結果
//TypeError: test is not a function

let

letに関しては変数でしか使わないので、変数での扱いを記載する

変数の再代入可能か?

→ 可能

ホイスティングされるか?

→ されない

ブロックスコープ

→ 適用される

変数の場合

再代入

正しく定義し、再代入をすると値を更新することができる

let sample = 1
//再代入
sample = 2

console.log(sample)

//結果
//2

ホイスティング

ホイスティングされない為、var の時のように定義が後の場合基本的に以下のようなエラーとなる

//定義の前に呼び出しを記述
console.log(sample)

//定義を呼び出しよりも後に記述
let sample = 1

//結果
//ReferenceError: sample is not defined

ブロックスコープ

ブロックスコープが適用されるため、以下のような記述では動作しない

if (true) {
  let sample = 1
}

console.log(sample)

//結果
// console.log(sample)
//             ^
// ReferenceError: sample is not defined

const

constに関しては変数と関数で扱うため両方の扱いを記載する

変数の再代入可能か?

→ 不可

ホイスティングされるか?

→ されない

ブロックスコープ

→ 適用される

変数の場合

再代入

letのように再代入しようとするとエラーとなる

const sample = 1
//再代入
sample = 2

console.log(sample)

//結果
// sample = 2
//        ^
// TypeError: Assignment to constant variable.

ホイスティング

ホイスティングされない為、var の時のように定義が後の場合基本的に以下のようなエラーとなる

//定義の前に呼び出しを記述
console.log(sample)

//定義を呼び出しよりも後に記述
const sample = 1

//結果
//ReferenceError: sample is not defined

ブロックスコープ

ブロックスコープが適用されるため、以下のような記述では動作しない

if (true) {
  const sample = 1
}

console.log(sample)

//結果
// console.log(sample)
//             ^
// ReferenceError: sample is not defined

関数の場合

以下のように定義を後に記述し、先に呼び出しを記述するとエラーとなる
ただし、varで以下のような関数式で定義した場合のエラーとでは違うエラー内容が
表示されている

//定義の前に呼び出しを記述
test()

//定義を呼び出しよりも後に記述
const test = () => {
  console.log('Hello')
}

//結果
// test()
// ^
// ReferenceError: Cannot access 'test' before initialization

以上が var,let,const の違いでした

ただ単に再代入できる・できないの違いしかないと思っていたので、まさかそんなところで違いがあるのかと驚きとヤバさを感じました ⚡

varが中々に酷い…


古いシステムとかだとvarが使われていたりするので、既存のソースをletconstに直した際は、本当にvarだった時の挙動を保証できているか入念にチェックした方が良いなと改めて思いました…💦
(varだと動くみたいな良く分からないソースになってる可能性もあるかも…)

補足

上記で説明した中でエラーの内容がどうやらブラウザエンジンごとに違うらしいです。
もし試すのであれば以下2つとかで試すと良さそう。

  • Chrome: V8
  • FireFox: Spider Monkey

(Edge は確か Chrome と同じ V8 だったはず…)

終わりに

今回参考にさせていただいたのは、CodeMafia さんが作成された udemy の「【JS】ガチで学びたい人のための JavaScript メカニズム」という動画教材です。

ここ最近フロントエンド開発で複雑な処理を実装する機会が結構あり、
javascript の挙動や仕様で沼ったことを機に改めて勉強し直すことにしました。
その中で、個人的に覚えておくべきだなと思うことを今後も備忘録代わりに記事にしようかと考えてます

こちらで引き続き javascript の理解を深めていきたいと思います m

では、また!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次