変数の宣言には違いがありますか:
var a=0; //1
...こちらです:
a=0; //2
...または:
window.a=0; //3
グローバルな範囲で?
ベストアンサー1
はい、いくつか違いはありますが、実際にはそれほど大きな違いではありません (ただし、#2 は除きます。a = 0;
これは、A) 実行しないことを強くお勧めします。また、B) 厳密モードではエラーになります)。
4 番目の方法があり、ES2015 (ES6) ではさらに 2 つあります。4 番目の方法は最後に追加しましたが、ES2015 の方法は #1 の後に挿入しました (理由は後でわかります)。つまり、次のようになります。
var a = 0; // 1
let a = 0; // 1.1 (new with ES2015)
const a = 0; // 1.2 (new with ES2015)
a = 0; // 2
window.a = 0; /*or*/ globalThis.a = 0; // 3
this.a = 0; // 4
これらの声明は、
1.var a = 0;
これにより、グローバル変数が作成され、これはグローバルオブジェクトwindow
ブラウザ(またはglobalThis
global は ES2020 で追加されました。または、this
グローバル スコープで を介して削除できます。他のプロパティとは異なり、このプロパティは を介して削除できませんdelete
。
仕様上、これは識別子バインディングを作成する。オブジェクト環境レコードのために地球環境グローバル オブジェクトは、グローバル環境のオブジェクト環境レコードの識別子バインディングが保持される場所であるため、これはグローバル オブジェクトのプロパティになります。これが、このプロパティが削除不可能な理由です。これは単なるプロパティではなく、識別子バインディングであり、識別子は削除できません。
バインディング (変数) は、コードの最初の行が実行される前に定義されます (var
以下の「いつ発生するか」を参照)。
これによって作成されるプロパティは列挙可能です (非常に古い IE8 以前を除く)。
1.1let a = 0;
これは、グローバル オブジェクトのプロパティではないグローバル変数を作成します。これは ES2015 以降の新機能です。
仕様上、これは識別子バインディングを作成する。宣言的環境記録グローバル環境ではなくオブジェクト環境レコードに分割されている点がグローバル環境の特徴です。環境記録1 つはグローバル オブジェクト (オブジェクト環境レコード)に含まれるすべての古い内容用で、もう 1 つはグローバル オブジェクトには含まれず、代わりにグローバル環境の宣言的let
環境レコードに含まれるすべての新しい内容 ( 、const
、および によって作成された関数) 用です。class
バインディングは、その外側のブロック内のステップバイステップのコードが実行される前に作成されます(この場合は、グローバル コードが実行される前)。ただし、ステップバイステップの実行がステートメントに到達するまで、バインディングにはいかなる方法でもアクセスlet
できません。実行がステートメントに到達するとlet
、変数にアクセスできるようになります (以下の「いつlet
、const
何が起こるか」を参照してください)。バインディングが作成されてから(スコープに入るとき)、アクセス可能になるまで(コード実行が に到達するまで) の時間は、 Temporal Dead Zonelet
[TMZ]と呼ばれます。バインディングがその状態にある間、バインディングからの読み取りやバインディングへの書き込みを試行すると、ランタイム エラーになります。
(バインディングがアクセス可能かどうかを表す仕様用語は「初期化されている」かどうかですが、「初期化されている」という使用法と、ステートメントlet
[let a = 10;
とlet a;
] に初期化子があることを混同しないでください。これらは無関係です。 によって定義された変数は、に到達するとlet a;
によって初期化されます。)undefined
let
1.2const a = 0;
グローバル オブジェクトのプロパティではないグローバル定数を作成します。
バインディングは、値を変更できないことを示すフラグがあることを除けば、バインディング (TMZ などを含む)const
とまったく同じです。その意味の 1 つは、 の初期値 (決して変更されない値) を提供するために、初期化子 ( 部分)を用意する必要があるということです。let
= value
const
を使用するとconst
、次の 3 つのことが実現します。
- 定数に代入しようとすると、ランタイム エラーが発生します (ほとんどの IDE では、それよりも積極的にフラグが立てられます)。
- 他のプログラマーのために、その不変の性質を文書化します。
- JavaScript エンジンが、 の値が変更されないことを前提に最適化できるようにします
const
(後で書き込まれるかどうかを追跡する必要がなく、つまり、実質的に定数であるかどうかを確認する必要がありません)。
const
の値が決して変わらないということは、 が参照するオブジェクトが不変であるという意味ではないことを理解することが重要です。そうではありません。 の値は変更できないため、別のオブジェクトを参照する (またはプリミティブを含む)とconst
いうだけです。const
// This is fine:
const x1 = {a: 1};
console.log(x1.a); // 1
x1.a = 2;
//^^^^^^−−− No problem, just changing the object's state, not the value in the `const` (the object reference)
console.log(x1.a); // 2
// This is not:
const x2 = {a: 1};
console.log(x2.a); // 1
x2 = {a: 2};
// ^−−−−−−− Error here ("TypeError: Assignment to constant variable"),
// you can't change the value of a `const`
console.log(x2.a);
2a = 0;
これをしないでください。これは完全に宣言されていない識別子への割り当てです。ルーズモード(ES5以前の唯一のモード)では、グローバルオブジェクトに暗黙的にプロパティを作成します。以前のブログではこれを暗黙のグローバルの恐怖ありがたいことに、彼らはそれを修正しました厳密モードは、ES5 で追加され、新しい種類のスコープ (モジュール内、コンストラクト内など) のデフォルトですclass
。厳密モードでは、宣言されていない識別子への割り当てが、常にエラーになるはずだったエラーになります。これは、厳密モードを使用するいくつかの理由の 1 つです。
通常のプロパティを作成するので、それが可能ですdelete
。
これによって作成されるプロパティは列挙可能です (非常に古い IE8 以前を除く)。
3window.a = 0;
またはglobalThis.a = 0;
window
これは、グローバル (ブラウザ上) またはグローバルオブジェクトを参照するグローバルを使用して、グローバル オブジェクトにプロパティを明示的に作成しますglobalThis
。これは通常のプロパティなので、削除できます。
このプロパティは列挙可能です (非常に古い IE8 以前でも)。
4this.a = 0;
3 番とまったく同じですが、グローバル オブジェクトをthis
グローバルやではなくwindow
で参照していますglobalThis
。これは、this
グローバル スコープでは が「グローバル」this
値であるため機能します。これは、厳密モードでも当てはまります。(厳密モードでは、を実行した場合など、this
を指定せずに関数を呼び出すときに使用される が変更されますが、グローバル スコープでは は変更されません。) 実際にはグローバルスコープである必要があることに注意してください。 のトップレベル スコープは、this
fn()
this
モジュールはグローバルスコープではありません (モジュールスコープです)。モジュールスコープではthis
ですundefined
。
プロパティの削除
「削除」または「除去」とはどういう意味ですかa
? まさにその通りです: キーワードを使用してプロパティを (完全に) 削除しますdelete
:
window.a = 0;
console.log(`"a" in window? ${"a" in window}`); // "a" in window? true
delete window.a;
console.log(`"a" in window? ${"a" in window}`); // "a" in window? false
delete
window
は、オブジェクトからプロパティを完全に削除します。を介して間接的に追加されたプロパティではこれを行うことはできません。var
は、delete
暗黙的に無視されるか、例外をスローします (厳密モードかどうかによって異なります)。
window
(注意: 非常に古い IE8 以前、および壊れた「互換性」モードの古い IE9 ~ IE11 では、プロパティの削除が許可されている場合でも、プロパティの削除はできません。)
いつvar
起こるか
前書き: var
新しいコードには不要です。代わりにlet
または を使用してください。ただし、遭遇した古いコードを理解する目的でconst
理解しておくと便利です。var
ステートメントを介して定義された変数は、実行コンテキスト内のステップバイステップのコードが実行されるvar
前に作成されるため、変数 (およびグローバル オブジェクト上のそのプロパティ) はステートメントよりかなり前に存在します。var
これはわかりにくいかもしれませんので、見てみましょう。ここでは、 と にアクセスしようとするコードがありa
、b
その後に中間でそれらを作成するコードがあり、その後に再度それらにアクセスしようとするコードがあります。
try {
console.log(a); // undefined
console.log(b); // ReferenceError: b is not defined
} catch (e) {
console.error(e);
}
var a = "ayy";
b = "bee"; // Don't do this, but I didn't want to use `let` or `const` in this example
try {
console.log(a); // "ayy"
console.log(b); // "bee"
} catch (e) {
console.error(e);
}
ご覧のとおり、識別子は最初の行が実行される前にa
定義されています (値 を使用) が、識別子は定義されていないため、その値を読み取ろうとすると になります。この文は実際には、異なるタイミングで2 つの異なることを行います。スコープに入ると、識別子が初期値( 部分) で定義され、その後コードの実行で到達すると、 の値(部分 ) が設定されます。はコードの最初の行が実行される前に定義されているため、これを使用できます (値を参照できます)。これは「巻き上げ」と呼ばれます。これは、部分がグローバル スコープまたは関数スコープの先頭に移動 (「巻き上げ」) されますが、部分は元の場所に残されるためです (undefined
b
ReferenceError
var a = "ayy";
undefined
var a
a
a = "ayy"
a
undefined
var
var a
a = "ayy"
誤解されたかわいそうなvar
私の貧弱な古いブログで。
いつlet
、何がconst
起こるか
let
およびは、いくつかの便利な点const
で とは異なりますvar
。質問に関連する点は、A) 定義するバインディングはステップバイステップのコードが実行される前に作成されますが、orステートメントに到達するまでアクセスできないこと、および B) 上で説明したように、グローバル スコープではグローバル オブジェクトにプロパティが作成されないことです。let
const
Re (A) では、次の使用法がvar
実行されます。
console.log(a); // undefined
var a = 0;
console.log(a); // 0
この使用法ではlet
エラーが発生します:
console.log(a); // ReferenceError: a is not defined
let a = 0;
console.log(a);
let
とconst
が と異なる他の 2 つの点 (var
質問とは実際には関係ありません) は次のとおりです。
var
は常に実行コンテキスト全体に適用されます (グローバル コード全体、またはそれが出現する関数内の関数コード全体。ブロック外にジャンプします)。ただし、 と は、let
それらが出現するブロックconst
内にのみ適用されます。つまり、 は関数 (またはグローバル) スコープを持ちますが、と はブロックスコープを持ちます。var
let
const
var a
同じコンテキスト内での繰り返しは無害ですが、let a
(またはconst a
) がある場合、別のlet a
またはconst a
または があるvar a
と構文エラーになります。
次の例は、 と が、そのブロック内のコードが実行される前にブロック内ですぐに有効になりますが、 or ステートメントまでアクセスできないことを示しlet
てconst
いlet
ますconst
。
let a = 0; // (Doesn't matter whether this is `let`, `const`, or `var` [or even `class` or `function`])
console.log(a); // 0
if (true) {
console.log(a); // ReferenceError: a is not defined
let a = 1;
console.log(a);
}
2 番目の文は、ブロックの外部から にconsole.log
アクセスする代わりに、そのブロック内ではブロックの後半で宣言されたを参照するため、失敗することに注意してください。ただし、文はその内部の の Temporal Dead Zone 内で発生するため、エラーが発生します。a
a
a
console.log
a
グローバルスコープの乱雑さを避ける - モジュールを使用する
グローバル スコープは非常に乱雑です。少なくとも次のようになります。
- 仕様によって作成された多くのグローバル変数(奇妙なことにキーワードではなくグローバルである や など、さまざまなグローバル関数
undefined
)NaN
- (ブラウザ上) を持つすべての DOM 要素と を持つ
id
多くのDOM 要素の変数name
(id
/name
値が有効な識別子である場合。そうでない場合は、 のプロパティでありwindow
、グローバル変数ではありません) - (ブラウザ上) 、、...
window
などの - 固有の変数name
location
self
- すべてのグローバルスコープ
var
ステートメントの変数 - すべてのグローバルスコープの
let
、、const
およびclass
ステートメントの変数
これらのグローバル変数はすべて、ブラウザでのこの典型的な例のように、コードと競合する機会に満ちています。
var name = 42;
console.log(name); // 42 - seems okay, but...
console.log(typeof name); // ...string?!?!!
なぜ はname
文字列なのでしょうか? これは、常に文字列であるウィンドウ オブジェクトの名前window
の のアクセサー プロパティであるためです。(宣言型環境レコードは概念的にオブジェクト環境レコード内にネストされているため、 の同等の は期待どおりに機能し、 で作成されたバインディングはアクセサー プロパティのバインディングをシャドウします。)let
name
let
name
可能な限り、混乱を招かないようにしてください。代わりにモジュールを使用してください。モジュール内のトップレベルのスコープはモジュール スコープexport
であり、グローバル スコープではありません。そのため、モジュール内の他のコードのみがトップレベルの宣言を参照します。およびを介してモジュール間で情報を共有できますimport
。
モジュールの前は、コードをラップした「スコープ」関数を使用していました。
// From the ES5 era
(function () {
var a = 0; // `a` is NOT a property of `window` now
function example() {
console.log(a); // Shows "0", because `example` can access `a`
}
example();
})();
モジュールにより、それは時代遅れになります。