Let、Var、およびConst:JavaScriptにおける変数の定義
2015年にES6の最終版が発表され、JavaScript変数を定義する新しい方法が登場しました。letキーワードはブロックスコープの変数を作成し、constは不変の値を指定します。これらの現代的な変数タイプが伝統的なvarとはどのように異なるのか、以下に説明します。
変数
ES6以前は、varが変数を定義する唯一の選択肢でした。varで作成した変数の値は自由に変更できます。また、変数そのものを再宣言することもできます。
var demo = "first value";
var demo = "second value";
demo = "third value";
example = "this throws an error - variable not declared";
varを使用すると、現在の関数にスコープされた変数が作成されます。関数の外で使用した場合、その変数はグローバルスコープになります。
変数の「スコープ」は、それがどこで使用できるかを説明します。関数スコープの変数は、それを定義する関数内のコードからアクセスできます。グローバル変数は、コード全体でアクセス可能です。
var myGlobal = "global";
function testA() {
var myFunctionScope = "testA";
console.log(myFunctionScope);
console.log(myGlobal);
}
function testB() {
myGlobal = "overwritten!";
console.log(myGlobal);
// console.log(myFunctionScope); // ERROR
}
testA();
testB();
testA();
この例では、グローバルスコープ変数と関数スコープ変数の違いが示されています。myGlobalは、両方から読み取られ(そして書き込まれ)ることができます。
testAそしてtestB。myFunctionScopeはのみ定義されている
testA, そのためtestBはそれにアクセスしようとするとエラーを投げます。この例が出力するのは次のとおりです:
testA
global
overwritten!
testA
overwritten!
myFunctionScopeの値は各関数内で別々に保持されます。myGlobalの値は、testBがそれを上書きする際に両方の関数で更新されます。
レッツ
新しいletキーワードは、varの現代的な代替手段です。以前varを書いていたすべての場所でletを採用することがよくできます。ただし、注目すべき重要な違いがあります。
最も重要なletの機能は、そのスコープです。変数は、全体の関数ではなく、個々のコードブロックにスコープされます。JavaScriptにおいて、ブロックとは波かっこで囲まれたコードのセクションです。各let変数は、そのブロック内のコードからのみアクセス可能です。
function letTest(x) {
let demo = "hello world";
if (x > 10) {
let y = 9000;
demo = "foobar";
}
console.log(demo); // "foobar"
console.log(y); // ERROR
}
この例では、if 文が新しいコードブロックを作成します。ブロックは親ブロックのスコープを引き継ぐので、demo 変数は利用可能なままです。y 変数は if 文にスコープされています。if ブロックの外部で y にアクセスしようとすると、未定義変数エラーが発生します。
varと同様に、letで作成された変数もいつでも値を変更できます。ただし、再宣言はできません - 同じブロック内で同じ名前を使用してletを2回使うとエラーになります。
let demo = "test";
demo = "example";
let demo = "test"; // ERROR
例外として、ネストされたスコープで変数を再宣言する場合があります。ブロックレベルのスコープのルールにより、これは許可されており、同じ識別子を持つ2つの別々の変数が存在することになります。
let foo = "bar";
if (true) {
let foo = "foobar";
console.log(foo);
}
console.log(foo);
上記の例では foobar bar が出力されます。foo 変数は if ブロック内で再宣言されますが、外側のスコープの foo 変数には影響しません。そのため、内部ブロックから外部変数を参照する能力は失われます。
定数
constキーワードは別のES6の追加です。letのようにブロックスコープです。constは「定数」の略で、決して変更されない不変の値に使用されます。const変数の値を更新しようとすると、常にエラーが発生します。
const foo = "bar";
const foo = "foobar"; // ERROR
その結果、const 変数には常に値で初期化する必要があります。後で値を設定することは許可されていません。
let demoLet; // OK
var demoVar; // OK
const demoConst; // ERROR
const demoConst = "value"; // OK
技術的には、constは定数の値を定義するものではありません。実際には、値への定数参照を作成します。これによる効果は、constに割り当てられたオブジェクトのプロパティをまだ更新できるということです。エラーは、代入の左辺にconstを使用した場合のみに発生します。
どの変数型を使用すべきですか?
JavaScriptコード内のほとんどの一般的な目的の変数にはletを採用すべきです。ブロックレベルのスコープと再宣言の禁止は、エラーをキャッチし、意図しない上書きを避けるのに役立ちます。
letを使用することで、変数の「漏れ」を防ぎ、変数が意図されていないスコープでアクセスされるのを防ぐことができます。クラシックな例は、ループ内のイテレータです:
for (var i = 0; i <= 10; i++) {
// do something
}
console.log(i);
これにより、10がコンソールに出力されます。代わりにletを使用すると、undefinedが出力されます。なぜなら、i変数はifのスコープの外ではアクセスできないからです。このようなシナリオでは、通常これが望ましい結果です。
ループはまた、var の再割り当ての危険性を示しています:
for (var i = 0; i <= 10; i++) {
setTimeout(() => console.log(i), 1000);
}
初めて見ると、このコードは1から10までの数字を出力するはずに見えます。でも、10 が10回ログされます。setTimeout() は非同期で、コールバック内の i はスコープに束縛されています。ループ内で var i が使われているため、i 変数は各反復ごとに新しい値を取得します。タイマーのコールバックが実行されるときには、すでに10回の反復が完了しており、i は常に最終値 - この場合は 10 に解決されます。
let iを使用すると、ループの各反復ごとにiという新しい変数が宣言されます。それぞれの変数は、自身の反復が完了した後も自身の値を保持し、期待されるログ出力を得ることができます。
Letを使うべきでない時
使用すべきではないシナリオがあり、letを使うべきではありません。変わることがないとわかっている変数を作成するときは、constを使うべきです。バンドラーや静的解析ツールは、意図せずその値を再代入しようとした場合に警告を出してくれるでしょう。
変数はできる限り不変に保つことが一般的に良いアイデアです。これにより、意図しない上書きによって引き起こされるバグを排除するのに役立ちます。constを使うことも、値が変更されないことを明示することで、コードベース内での意図を示すのに役立ちます。
ES6の機能であるletやconstは、現在、最新のブラウザで普遍的にサポートされています。完全なES6サポートを持たない古いブラウザ、特にInternet Explorer 11でも利用可能です。古いプラットフォームをターゲットにしない限り、これらが利用できるかどうかを心配する必要はありません。letとconstを使用することで、コードをよりクリーンに保ち、隠れたバグのリスクを減らすことができます。
