はじめに
なんとなーくでlet
/const
を使い分けていたが
間違った理解をしていたので、本気で使い分ける方法をまとめてみました。
ざっくりした結論
- ほぼ全部
const
で定義できる - 数え上げのみ
let
- varはもう使わない
細かい仕様の説明
var
/const
/let
の違い
const | let | var | |
---|---|---|---|
再宣言 | × | × | ○ |
再代入 | × | ○ | ○ |
スコープ | ブロック | ブロック | 関数 |
ホイスティング | エラー | エラー | undefined |
再宣言
一度宣言した変数を、同じ変数名で宣言し直すことを再宣言
といいます。
再宣言可能なvar
で再宣言した場合、後に宣言した変数が適用され、
再宣言不可能なlet/const
で再宣言した場合、エラーになります。
var
では、予期しない再宣言が起こりうるため危険です。
// var
var a = 0;
var a = 1;
console.log(a) // `1`と出力される
// let
let b = 0;
let b = 1; // SyntaxError: Identifier 'b' has already been declared
// const
const c = 0;
const c = 1; // SyntaxError: Identifier 'c' has already been declared
再代入
宣言した変数に値を設定し直すことを再代入
といいます。
再代入可能なvar/let
で再代入した場合、値が再代入され、
再代入不可能なconst
で再代入した場合、エラーになります。
// var
var a = 0;
a = 1;
console.log(a) // `1`と出力される
// let
let b = 0;
b = 1;
console.log(b) // `1`と出力される
// const
const c = 0;
c = 1; // Assignment to constant variable.
スコープ
実行中のコードから参照できる範囲をスコープ
といいます。
const/let
はブロックスコープ({}
で囲われた部分 - if文やfor文など)が適用されますが、var
はブロックスコープが適用されません。
※ 関数スコープ(関数宣言の{}
)は、var/const/let
すべてに適用されます。
/*
* var
*/
{
var a = 0;
}
// ブロックスコープが適用されないため、ブロック外でも値の参照が可能
console.log(a); // 0
/*
* let
*/
{
// ブロックスコープにより、再宣言にならない。
let a = 1;
console.log(a); // 1
}
// letはブロックスコープであり参照できないため、varで宣言した値が参照される。
console.log(a); // 0
/*
* const
*/
{
const b = 2;
console.log(b); //2
}
console.log(b) // b is not defined
ホイスティング(変数の巻き上げ)
変数宣言が常に関数の先頭で行われたことにされる挙動を**ホイスティング(変数の巻き上げ)**といいいます。
// var
{
console.log(a); // undefined
var a = 0;
console.log(a); // 0
}
// let / const
{
console.log(b); // Cannot access 'b' before initialization
const b = 0;
console.log(b); // 0
}
データ型の種類と参照方法
var
/const
/let
の変数宣言を使い分けるには、データ型の種類と参照方法を知っておく必要があります。
データ型の種類
データ型には、大きく分けてプリミティブ型とオブジェクトがあります。
- プリミティブ型(基本型)
- Boolean - true または false
- Number - 数値
- String - 文字列
- null - 空(参照を保持していない状態)
- undefined - 未定義
- BigInt (長整数) - 桁が多い数値(ES6)
- Symbol (シンボル) - 一意の値(ES6)
- オブジェクト(複合型) - プリミティブ型以外全部
- オブジェクトリテラル
- 配列リテラル など
データ型の参照
プリミティブ型とオブジェクトでは、参照方法が異なります。
プリミティブ型の場合
再代入の際に参照先が変更されます。
オブジェクトの場合
変数に対して参照するオブジェクトが変更された際に再代入になります。
オブジェクト内のキーや値が変更されても、変数に対する参照先が変わらないため再代入にはなりません。
もう一度、結論
- ほぼ全部
const
で定義できる- オブジェクトや配列の値の変更は再代入にはならないため。
- プリミティブ型を再代入したい場合は
let
- varはもう使わない