はじめに
過去の自分は、const
で宣言した変数の値は変えることができないという理解をしていて
「この変数は値が変わるかもしれないから一応let
で変数宣言しよ」
とlet
ばかりを使っていました。
しかしこの理解が誤っていることを最近知り、
「あのlet
で宣言してた変数全部const
でよかったじゃん!」
「定数管理したくてconst
で宣言したけど意味なかったなあ」
と後悔することになったので、反省を込めてconst
についてまとめてみました。
結論
結論から言うと、
const
は値の参照を読み取り専用で作成します。
別の言い方をすれば
const
で宣言された変数は、あくまでもアドレス(参照先)の変更ができないだけとなります。
つまりアドレスが変更されない処理であれば、const
で宣言された変数の値を変えることは可能となります。いきなり言われてもピンと来ない方もいらっしゃると思うので、変数に代入されたデータがミュータブル(変更可能)な場合とイミュータブル(変更不可能)な場合で比較しながら以下で具体的な挙動を説明します。
(※参考資料: ミュータブルな型とイミュータブルな型の相違を知ろう)
具体的な挙動
変数にミュータブルなデータが代入された場合
const array = [1,2,3]
array[0] = 10
console.log(array) //=> [10, 2, 3]
上記のようにconst
で宣言された変数であっても値は問題なく変更できます。
これは変数に格納されているデータがミュータブルなものであり、値を変更してもアドレス(参照先)が変更されないからです。
しかし、例えば以下のような再代入などアドレスが変更されるようなコードは実行できません。
const array = [1,2,3]
array = [10,20.30] //=> Uncaught TypeError: Assignment to constant variable.
変数にイミュータブルなデータが代入された場合
const number = 10
number += 10 //=> Uncaught TypeError: Assignment to constant variable.
const str = 'str'
str += 'ing' //=> Uncaught TypeError: Assignment to constant variable.
上記のように、値の変更ができません。
これはイミュータブルなデータの値を変更すると、アドレスも変更されるからです。
以上のことから、
「const
で宣言された変数は、アドレスの変更ができない。」
だけであり、
変数にミュータブルなデータが格納されている場合値を変更できるため、
「この変数は値が変わるからconstで変数宣言出来ないな」は不十分な表現であったことがわかります。
補足
constで宣言された変数の中身がミュータブルなデータであっても値の変更を不可能にしたい場合は、以下のようにすることで実装できます。
const array = [1, 2, 3]
Object.freeze(array)
array[0] = 10
console.log(array) //=> [1,2,3]
また、凍結したデータの値を変更する処理があった場合に例外を発生させたいのであれば、厳格モードを使うことで以下のような例外を発生させることができます。
'use strict' // 厳格モード使用
const array = [1, 2, 3]
Object.freeze(array)
array[0] = 10 //=> Uncaught TypeError: Cannot assign to read only property '0' of object '[object Array]'