【JavaScript】変数宣言var/let/constの使い分け
更新日:2023/10/10
JavaScriptには変数の宣言方法がvarとlet、それにconstの3種類あります。
今まで僕は、ほぼletのみを使用していました。
ですがどうやら、それは正しくはなかったようです。
そこで今回は、JavaScriptの変数宣言について調べました。
2023/10 構成と内容を整理しました。
変数宣言で使用するキーワードの選び方
いままで変数宣言のキーワードを適当に使っていましたが、次のように選ぶのが今のJavaScriptの流れのようです。
- varは使用しない
- 定数的に使用する変数はconst
- 値を変更(代入)するものはlet
- オブジェクトで、プロパティ値やメンバを変更するだけならconst
特に(4)です。
「オブジェクトは、変数に再代入しなくてもプロパティ値を変更するときは let だよね」
と思っていましたが、constが正解でした。
どうしてなのか、少し詳しく解説していきます。
let/constの特徴
JavaScriptの変数宣言は3種類ありますが、推奨されているのはletとconstです。
まずは、この二つについて紹介します。
ブロックスコープで評価
letとconstはブロックスコープで評価されます。
ブロックとは、 { } の範囲内です。
この範囲内で同名の変数は同じものだということです。
function f() {
let x = 100;
if( x > 0 ) {
let x = 300;
}
for( let i = 0 ; i < 3 ; i ++ ) {
x++;
}
console.log( x ); // 103
}
f();
if文の中のxはletで定義しているので外側のxとは別物です。
そのため代入しても、外側のxの値は変更されません。
ブロック内で再定義するとエラーになる
letとconstは同レベルのブロックスコープ内で再定義するとエラーになります。
function f() {
let x = 100;
let x = 200; // redeclaration of let x
const x = 300; // redeclaration of let x
}
下位レベルでの定義はできます。
function f() {
let x = 100;
{
let x = 200; // ok!
}
}
ブロックごとの評価例
変数がブロックごとに評価されている例を見てみます。
let count = 0;
for( let i = 0 ; i < 2 ; i ++ ){
for( let i = 0 ; i < 2 ; i ++ ){
++count;
}
}
console.log( count ); // 4
上の例は、入れ子になったforループでそれぞれ変数 i を定義しています。
それぞれの変数 i は別物なので、互いに干渉せずにループできています。
let/constの違いは再代入できるかどうか
letは再代入できますが、constはできません。
次のコードのように、letは値を変更できます。
let x = 100;
x = 300;
x = "hello";
constで定義した変数は、後から値を変更できません。
変更すると、エラーが表示されて止まります。
const x = 100;
x = 300; // TypeError: invalid assignment to const 'x'
ただしオブジェクトのプロパティを変更することはできます。
const y = {
x:10
};
y.x = 400; // プロパティの変更できる
y.x2 = 500; // プロパティの追加もできる
y = { z :10 }; // 変数そのものの変更はできない
//TypeError: invalid assignment to const 'y'
constでオブジェクトのプロパティを変更できる理由
なぜconstで定義したオブジェクトのプロパティを変更できるのでしょうか。
それは変数がオブジェクトの実体と関連付けられているだけ、だからです。
letは、今関連付けを変更できます。
しかしconstは、変更できません。
どちらにしても、オブジェクトの実体の中身までは干渉していません。
そのためletとconst関係なく、オブジェクトのプロパティを変更できるのです。
varの特徴
varの特徴と、推奨されない理由をお伝えします。
varは関数スコープ
varは関数スコープで評価されます。
関数スコープとは、関数内において同じ名前の変数は同じものだということです。
var x = "xxxx";
function f() {
var x = 100;
if( x > 0 ) {
var x = 300;
}
for( var i = 0 ; i < 3 ; i ++ ) {
var x = 400;
}
console.log( x ); // 400
}
f();
console.log( x ); // xxxx ← 関数内のxとは別物
関数内の変数xは、巻き上げられて次のようなコードになります。
参考:【JavaScript】 変数・関数の巻き上げってなんだ?
function f() {
var x;
x = 100;
if( x > 0 ) {
x = 300;
}
for( var i = 0 ; i < 3 ; i ++ ) {
x = 400;
}
console.log( x ); // 400
}
xは、varで3回定義されていますが、実際は一回だとわかりますね。
この程度の短いコードなら変数名が重複しませんが、長くなると重複する可能性があります。
意図せず同名変数を定義する可能性があることも、varが推奨されない理由です。
グローバルオブジェクトのプロパティになる
グローバルスコープでvar定義した変数は、グローバルオブジェクトのプロパティとなります。
次の例を見てください。
<script>
var a = 10;
console.log( globalThis.a );
<script>
スクリプトタグの内側はグローバルスコープです。
ここでvar定義した変数aは、globalThis(グローバルオブジェクト)のプロパティになっています。
これが問題になるのが、次のケースです。
<script>
console.log( window.addEventListener );
// 結果: function addEventListener()
</script>
<script>
// window.addEventListener を上書き
var addEventListener = 10;
console.log( window.addEventListener );
// 結果: 10
</script>
<script>
// 以降のスクリプトタグにも影響する
console.log( globalThis.addEventListener );
// 結果:10
</script>
ブラウザではwindowオブジェクトとglobalThisが同値です。
addEventListenerは、windowオブジェクトが持っているメソッドです。
しかし同名の変数をvarで定義したために、window.addEventListenerが上書きされています。
addEventListenerはよく使うメソッドなのでこのようなケースはあまりないですが、windowオブジェクトは無数にメソッドがあります。
意図せずグローバルを汚染する危険性があるのです。
この点も、varが推奨されない理由です。
let/const/varの使い分け
varは使用しない
現在ではvarを使用する意味はありません。
JavScriptは関数スコープで評価されるvarしかありませんでした。
しかし意図せず同名の変数を定義してしまうなど、バグを生み出す可能性が大きいという問題点があります。
そこでブロックスコープで評価されるletとconstが、JavaScriptの実質的な仕様であるECMAScriptの2015から追加されたのです。
参考:【JavaScript】 ECMAScriptってなに?
値を変更しない変数はconstを使う
定数的なものはもちろん、関数内で再代入しないものにはconstを使用します。
再代入をするとエラーで停止するので、コーディング上のミスを防ぐことができます。
constの使用例
■定数として利用
const VERSION = "1.1";
■DOM要素を取得する
const buttom = document.getElementById("buttom");
buttom.addEventListener( "click" , function(){
buttom.style.color = "red";
} );
■再代入しないオブジェクト(プロパティの操作・追加はおこなう)
const obj = { x : 100 };
obj.x = 200;
obj.func1 = function(){ };
■関数・アロー関数
const f = function(){
// 処理
};
const f2 = () => { // アロー関数
// 処理
};
再代入する変数はletを使う
ワーク的な変数など、再代入するものだけletを使用します。
letの使用例
■再代入
let a = 100;
a = 200;
a = { x:100 };
let b = "hello";
a = b;
■再代入するオブジェクト
let a = { x:200 };
a = { y:300 };
const b = { x:400 };
a = b;
■forループ
for( let i = 0 ; i < 3 ; i ++ ){
// 処理
}
更新日:2023/10/10
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。