1. ホーム
  2. ジャバスクリプト

[解決済み】Javascriptの変数宣言の構文の違い(グローバル変数を含む)?

2022-03-30 20:42:10

質問

変数を宣言するのとしないのとでは、何か違いがあるのでしょうか?

var a=0; //1

...このように

a=0; //2

...または

window.a=0; //3

グローバルスコープで?

解決方法は?

はい、いくつかの違いがありますが、実用上は通常大きな違いではありません。

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;  // 3
this.a = 0;    // 4

これらの記述の説明

#1 var a = 0;

のプロパティでもあるグローバル変数が作成されます。 グローバルオブジェクト としてアクセスします。 window ブラウザ上で(または this をグローバルスコープに設定することができます(非制限的なコード)。他のいくつかのプロパティとは異なり、このプロパティは delete .

仕様の用語では、これは 識別子バインディング を指定します。 オブジェクト 環境記録 に対して グローバル環境 . なぜなら、グローバルオブジェクトは、グローバル環境のオブジェクトEnvironment Recordの識別子バインディングが保持される場所だからです。このため、このプロパティは削除できないようになっています。これは単なるプロパティではなく、識別子バインディングなのです。

バインディング(変数)は、コードの最初の行が実行される前に定義されます("When var happens"以下同様)。

なお、IE8以前では、このプロパティは window 列挙可能 (で表示されない)。 for..in ステートメント)。IE9、Chrome、Firefox、Operaでは、列挙可能です。


#1.1 let a = 0;

これは、グローバル変数を作成します。 ではない グローバルオブジェクトのプロパティです。これはES2015の時点では新しいことです。

仕様の用語で言うと、これは 宣言的 グローバル環境用の環境レコードではなく オブジェクト 環境レコードです。グローバル環境は環境レコードが分割されているのが特徴で、1つはグローバルオブジェクトにある古いもの全てに対応するものです( オブジェクト 環境レコード)と、すべての新しいもの( let , const で作成された関数、および class ) のうち、グローバルオブジェクトに乗らないものです。

バインディングは 作成 その囲むブロック内のステップバイステップのコードが実行される前に(この場合、グローバルコードが実行される前に)、しかし、それは アクセス可能 に到達するまでは、どのような形であれ、その実行は継続されます。 let ステートメントを使用します。実行が let ステートメントを使用すると、その変数にアクセスできるようになります。(参照: "When letconst 以下、happen" とします)。


#1.2 const a = 0;

グローバルオブジェクトのプロパティではないグローバル定数を作成します。

const は、まさに let ただし、イニシャライザを用意しなければならない ( = value の部分)があり、一度作成した定数の値を変更することはできません。裏を返せば、これはまさに let というフラグを識別子のバインディングにつけて、その値を変更できないようにしています。使用方法 const は、3つのことをやってくれます。

  1. 定数に代入しようとすると、パースタイムエラーになるようにする。
  2. 他のプログラマーのために、その不変の性質を文書化する。
  3. JavaScriptエンジンが変化しないことを前提に、最適化させる。

#2 a = 0;

これは、グローバルオブジェクトにプロパティを作成します。 暗黙のうちに . 通常のプロパティなので、削除しても問題ありません。私がお勧めするのは ではなく これは、後であなたのコードを読む人にとって不明瞭になる可能性があります。ES5のストリクトモードを使用している場合、このようなこと(存在しない変数への代入)はエラーになります。これは、ストリクトモードを使用するいくつかの理由のうちの一つです。

そして興味深いことに、やはりIE8以前では、作成されたプロパティは 列挙可能 (で表示されない)。 for..in ステートメント)。特に以下の3番を考えると、それは奇妙なことです。


#3 window.a = 0;

これは、グローバルオブジェクトのプロパティを明示的に作成するものです。 window グローバルオブジェクトを参照するグローバル変数 (ブラウザの場合。ブラウザ以外の環境では、同等のグローバル変数、たとえば global NodeJSの場合)。通常のプロパティなので、削除しても大丈夫です。

このプロパティ が、IE8以前、そして私が試した他のすべてのブラウザで、列挙可能です。


#4 this.a = 0;

3と全く同じですが、グローバルオブジェクトを参照するために this ではなく、グローバルな window . しかし、これはストリクトモードでは機能しません。ストリクトモードでは、グローバルなコードです。 this はグローバルオブジェクトへの参照を持っていない (それは値として undefined となります。)


プロパティの削除

削除とはどういう意味ですか? a ? まさにその通りです。プロパティを(完全に)削除するには delete というキーワードがあります。

window.a = 0;
display("'a' in window? " + ('a' in window)); // displays "true"
delete window.a;
display("'a' in window? " + ('a' in window)); // displays "false"

delete は、オブジェクトからプロパティを完全に削除します。に追加されたプロパティでは、そのようなことはできません。 window を経由して間接的に var は、その delete は黙って無視されるか、例外が投げられます (JavaScriptの実装と、ストリクトモードかどうかに依存します)。

警告 : IE8を再び(おそらくそれ以前も、IE9-IE11を壊れた"compatibility"モードでも)。のプロパティを削除できない。 window オブジェクトを使用することが許可されている場合でもです。さらに悪いことに、それは 例外 を試すと ( この実験をやってみる IE8や他のブラウザでは)。から削除すると window オブジェクトを使用する場合は、守りを固める必要があります。

try {
    delete window.prop;
}
catch (e) {
    window.prop = undefined;
}

これはプロパティの削除を試み、例外が発生した場合は、次善の策を講じてプロパティを undefined .

これは のみ が適用されます。 window オブジェクトは、(私の知る限り)IE8以前(または壊れた"互換性"モードのIE9-IE11)のみで使用可能です。他のブラウザでは window プロパティは、上記のルールに従います。


いつ var 起こる

で定義された変数は var ステートメントが作成される前に 任意の 実行コンテキスト内のステップバイステップのコードが実行されるため、このプロパティはよく存在する var ステートメントを使用します。

これは分かりにくいので、見てみましょう。

display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "undefined"
display("bar in window? " + ('bar' in window)); // displays "false"
display("window.bar = " + window.bar);          // displays "undefined"
var foo = "f";
bar = "b";
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "f"
display("bar in window? " + ('bar' in window)); // displays "true"
display("window.bar = " + window.bar);          // displays "b"

ライブの例です。

display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "undefined"
display("bar in window? " + ('bar' in window)); // displays "false"
display("window.bar = " + window.bar);          // displays "undefined"
var foo = "f";
bar = "b";
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "f"
display("bar in window? " + ('bar' in window)); // displays "true"
display("window.bar = " + window.bar);          // displays "b"

function display(msg) {
  var p = document.createElement('p');
  p.innerHTML = msg;
  document.body.appendChild(p);
}

ご覧のように、シンボル foo は最初の行の前に定義されていますが、シンボル bar はない。ここで var foo = "f"; シンボルの定義(これはコードの最初の行が実行される前に行われます)と、そのシンボルへの代入(これはステップバイステップの流れの中でその行がある場所で行われます)です。これは、「"」と呼ばれるものです。 var を使用するためです。 var foo 部分はスコープの最上部に移動 ("hoisted") しますが foo = "f" の部分は元の場所に残されます。(参照 貧乏人の勘違い var 私の貧弱な小さなブログで)


いつ letconst 起こる

letconst とは異なります。 var を、いくつかの点から説明します。質問に関連する方法は、それらが定義するバインディングはステップバイステップのコードが実行される前に作成されますが、それは アクセス可能 になるまで let または const 文に到達したとき。

だから、これが実行されている間は

display(a);    // undefined
var a = 0;
display(a);    // 0

これはエラーを投げます。

display(a);    // ReferenceError: a is not defined
let a = 0;
display(a);

という他の2つの方法があります。 letconst とは異なります。 var という、質問とはあまり関係のないものがあります。

  1. var は常に実行コンテキスト全体 (グローバルコード全体、またはそれが現れる関数内の関数コード全体) に適用されますが letconst の中だけに適用されます。 ブロック が表示されます。つまり var は関数(またはグローバル)スコープを持ちますが letconst はブロックスコープを持ちます。

  2. 繰り返し var a を同じコンテキストで使用することは無害ですが、もしあなたが let a (または const a ) を持つことで、別の let a または const a または var a はシンタックスエラーです。

以下はそのデモ例です。 letconst は、そのブロック内のコードが実行される前に、そのブロック内ですぐに有効になりますが let または const ステートメントを使用します。

var a = 0;
console.log(a);
if (true)
{
  console.log(a); // ReferenceError: a is not defined
  let a = 1;
  console.log(a);
}

なお、2番目の console.log にアクセスするのではなく、失敗します。 a をブロックの外から実行します。


オフトピです。グローバルオブジェクトを散らかさないようにする ( window )

window オブジェクトはプロパティでとてもとてもごちゃごちゃしています。可能な限り、このようなごちゃごちゃを増やさないことを強くお勧めします。その代わりに、シンボルを小さなパッケージにまとめ、次のようにエクスポートしてください。 最も に1つのシンボルを window オブジェクトを作成します。(私は頻繁に 任意 シンボルを window オブジェクトを作成します)。シンボルを格納するために、すべてのコードを格納する関数を使用することができます。その関数は、あなたが望むなら、匿名にすることができます。

(function() {
    var a = 0; // `a` is NOT a property of `window` now

    function foo() {
        alert(a);   // Alerts "0", because `foo` can access `a`
    }
})();

この例では、関数を定義してすぐに実行させています( () を最後に追加します)。

このように使われる関数は、よく スコープ関数 . スコープ関数の中で定義された関数は、スコープ関数で定義された変数にアクセスできます。 クロージャ をそのデータの上に置く(参照。 クロージャは複雑ではない を私の貧弱な小さなブログで紹介します)。