Javascriptの変数宣言構文の違い(グローバル変数を含む)?


292

変数の宣言に違いはありますか?

var a=0; //1

...こちらです:

a=0; //2

...または:

window.a=0; //3

グローバルスコープで?


2
AFAIK var a = 0; 別のjsファイルで宣言されている別の外部jsファイルを介して変数にアクセスすると、IEで機能しません
Aivan Monceller

window.aについては知りませんが、他の2つの方法はグローバルスコープでも同じです。
プログラマー

1
@AivanMonceller本当に?リンクしてください。
レイノス

@Raynos、自分のウェブサイトで体験しています。具体的にはIE6。外部のjsファイルにあるvar enumを表示できませんでした。それをHTMLファイルのインラインJavaScriptとして参照しています
Aivan Monceller

@Ashwiniグローバルスコープでは、ウィンドウは(ブラウザーの)グローバルオブジェクトです。変数a = 1; console.log(a); console.log(win
leebriggs、2011

回答:


557

はい、実際には大きな違いはありませんが、いくつかの違いがあります。

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

仕様では、グローバル環境のオブジェクト環境レコードに識別子バインディングを作成します。グローバルオブジェクトはグローバル環境のオブジェクトである環境レコードの識別子バインディングが保持されるため、グローバルオブジェクトのプロパティになります。これがプロパティが削除不可である理由です。これは単なるプロパティではなく、識別子バインディングです。

バインディング(変数)は、コードの最初の行が実行される前に定義されます(以下の「いつvar起こるか」を参照)。

IE8以前では、で作成されたプロパティwindow列挙可能ではないことに注意してください(for..inステートメントには表示されません)。IE9、Chrome、Firefox、Operaでは列挙可能です。


#1.1 let a = 0;

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

仕様上、オブジェクトの環境レコードではなく、グローバル環境の宣言型環境レコードに識別子バインディングを作成します。地球環境は、スプリット環境録音、グローバルオブジェクト(上行くすべての古いもののために有するもので一意であるオブジェクトのすべての新しいもの(のための環境レコード)と別のletconst、およびによって作成された機能をclass、そうでありません)グローバルオブジェクトに行きます。

バインディングは、それを囲むブロック内のステップバイステップコードが実行される前に(この場合は、グローバルコードが実行される前に)作成されますが、ステップバイステップ実行がステートメントに到達するまでは、アクセスできませんlet。実行がletステートメントに到達すると、変数にアクセスできます。(以下の「いつletconstどうなるか」を参照してください。)


#1.2 const a = 0;

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

constletイニシャライザ(= valueパーツ)を提供する必要があることと、作成された定数の値を変更できないことを除いて、まったく同じです。裏では、それはまったく同じですletが、値を変更できないことを示すフラグが識別子バインディングにあります。を使用constすると、次の3つのことが行われます。

  1. 定数に割り当てようとすると、解析時間エラーになります。
  2. 他のプログラマーのための不変の性質を文書化します。
  3. JavaScriptエンジンが変更されないことに基づいて最適化できるようにします。

#2 a = 0;

これにより、グローバルオブジェクトにプロパティが暗黙的に作成されます。これは通常のプロパティなので、削除できます。私はこれを行わないことをお勧めします。後でコードを読む人にとっては不明確になる可能性があります。ES5のストリクトモードを使用している場合、これを行う(存在しない変数に割り当てる)とエラーになります。strictモードを使用するのは、いくつかの理由の1つです。

そして興味深いことに、再びIE8以前では、プロパティは列挙可能ではありません(for..inステートメントには表示されません)。これは奇妙で、特に以下の#3に示されています。


#3 window.a = 0;

これにより、グローバルオブジェクトwindowを参照するグローバルを使用して、グローバルオブジェクトにプロパティが明示的に作成されます(ブラウザー上。一部の非ブラウザー環境には、globalNodeJS などの同等のグローバル変数があります)。これは通常のプロパティなので、削除できます。

このプロパティ、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を介して間接的にvardeleteのいずれか黙って無視されるか(JavaScriptの実装に、あなたがstrictモードにいるかによって)例外をスローしています。

警告:再びIE8(おそらく以前、および壊れた「互換性」モードのIE9-IE11):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"

ライブの例:

ご覧のとおり、シンボルfooは最初の行の前に定義されていますが、シンボルは定義されてbarいません。var foo = "f";ステートメントがある場合、実際には2つのことが行われます。シンボルの定義。コードの最初の行が実行される前に行われます。そして、そのシンボルへの割り当てを行います。これは、ラインがステップバイステップのフローにある場合に発生します。これはvarvar fooパーツがスコープの最上部に移動(「巻き上げ」)されますが、foo = "f"パーツは元の場所に残るため、「巻き上げ」と呼ばれます。(貧血の小さなブログで誤解さvarれている貧しい人々を参照してください。)


ときletconst起こります

letそして、constは異なっているvarいくつかの方法インチ 問題に関連する方法は、それらが定義するバインディングは、ステップバイステップのコードが実行される前に作成されますが、or ステートメントに到達するまでアクセスできないということです。letconst

これが実行されている間:

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

これはエラーをスローします:

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

質問と実際には関係のない、letとのその他の2つのconst違いvarは次のとおりです。

  1. var常に(グローバルコードを通して、またはそれが現れる関数内の機能コード全体)全体の実行コンテキストに適用されますが、letおよびconst内のみ適用されますブロック、彼らが表示されます。つまり、var機能(またはグローバル)スコープletconst持っていますが、ブロックスコープを持っています。

  2. var a同じコンテキストで繰り返しても問題はありませんが、let a(またはconst a)がある場合、別のlet aまたはa const aまたはa var aがあると構文エラーになります。

次の例は、そのことletを示しておりconst、そのブロック内のコードが実行される前にブロックですぐに有効になりますが、letor constステートメントまでアクセスできません。

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

ブロックの外側からconsole.logにアクセスする代わりに、2番目が失敗することに注意してください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`
    }
})();

その例では、関数を定義して、すぐに(()最後に)実行します。

このように使用される関数は、スコープ関数と呼ばれることがよくあります。スコープ関数内で定義された関数は、スコープ関数で定義された変数にアクセスできます。これは、それらがそのデータのクロージャだからです(参照:貧弱な小さなブログでは、クロージャは複雑はありません)。


window['a']=0ウィンドウをマップとして使用していることを明確にするにはどうすればよいですか?あるwindow一部のブラウザでは、これを許可し、使用するために私を強制しないように特別なwindow.a
Jayen、2015

おそらく明確にする価値がある#3に関する1つの注意:window.a = 0;ブラウザー環境でのみ機能し、慣例によってのみ機能します。名前付き変数へのグローバルオブジェクトのバインドはwindowES仕様にないため、たとえばV8やNode.jsでは機能しませんが、this.a = 0;(グローバル実行コンテキストで呼び出されると)仕様で指定されているため、どの環境でも機能します。グローバルオブジェクトが存在する必要があります。Off-topicセクションのようにIIFEでコードをラップする場合は、this名前付きパラメーターとして渡すwindowglobal、グローバルオブジェクトへの直接参照を取得できます。
Sherlock_HJ

@Sherlock_HJ:「ブラウザ上」を追加しました。それも答えの早い方ですが、人々がスキップする場合に備えて私はそれを追加しました。現在、仕様に含まれています。つい先ほどですが、それを実行しないブラウザは見つかりません。附属書Bにはないので、少し驚いています。
TJクラウダー2016

@TJCrowder、したがって、で宣言されたグローバル変数はvar a = 0;自動的にグローバルオブジェクトのプロパティになります。var b = 0;関数宣言内で宣言した場合、それはいくつかの基になるオブジェクトのプロパティにもなりますか?
ezpresso 2016年

@ezpresso:いいえ、はい。それらはオブジェクトのプロパティになります(それらが表示されるExecutionContextVariableEnvironmentEnvironmentRecord、詳細はこちらこちら)。ただし、プログラムコードからそのオブジェクトに直接アクセスする方法はありません。
TJクラウダー2016年

40

シンプルに保つ:

a = 0

上記のコードはグローバルスコープ変数を提供します

var a = 0;

このコードは、現在のスコープで、およびその下で使用される変数を提供します

window.a = 0;

これは通常、グローバル変数と同じです。


「上のコードはグローバルスコープ変数を提供します」「このコードは現在のスコープで使用される変数を提供します」というステートメントを一緒にすると、最初の行を使用a して、現在のスコープ。あなたはできる。また、「グローバル変数」の使用は少しずれています。「グローバル変数」と言う2つの場所は、言わない場所よりもグローバルではありません。
TJクラウダー

グローバル自体は、私が現在のスコープについて言及した場所を含む、どこでも変数にアクセス/読み取り/書き込みできることを意味します。そして、もしあなたがwindow.aと 'a'がスクリプトでグローバルにならないことを提案したら、あなたは100%間違っています。
Umair Jabbar、2011

3
@Umair:「グローバル自体は、どこからでも変数にアクセス/読み取り/書き込みできることを意味します」そうです。繰り返しますが、最初と最後は、真ん中よりも「グローバル」であるように呼びかけているようですが、もちろんそうではありません。
TJクラウダー2011

4
真ん中のものは関数内で使用されると見なされますが、メインスコープの下で使用される場合はすべて同じです。関数内でvarを使用することが私の想定
でした

4
@Umair:「関数内でvarを使用することが私の想定でした」ああ、そうです。しかし、それは問題ではありません。質問では、「グローバルスコープ」と非常に明確に述べています。仮定を変更する場合(これは十分に公正であり、より一般的なポイントを拡張して説明するため)、答えでそれを行っていることを明確にする必要があります。
TJクラウダー、2011

10
<title>Index.html</title>
<script>
    var varDeclaration = true;
    noVarDeclaration = true;
    window.hungOnWindow = true;
    document.hungOnDocument = true;
</script>
<script src="external.js"></script>

/* external.js */

console.info(varDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(noVarDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(window.hungOnWindow == true); // could be .log, alert etc
// returns true in IE8

console.info(document.hungOnDocument == true); // could be .log, alert etc
// returns ??? in IE8 (untested!)  *I personally find this more clugy than hanging off window obj

すべての変数がデフォルトでオフになっているグローバルオブジェクトはありますか?例: 'globals.noVar宣言'


とても素敵な探索。window.*宣言を使用するための明確なガイド。この宣言は、コードのコピーアンドペーストに対して最も安全に見え、また明確です。
Dan

7

優れた答えにBassed TJクラウダー:(オフトピック:避ける乱雑window

これは彼のアイデアの例です:

HTML

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="init.js"></script>
    <script type="text/javascript">
      MYLIBRARY.init(["firstValue", 2, "thirdValue"]);
    </script>
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Hello !</h1>
  </body>    
</html>

init.jsこの回答に基づく)

var MYLIBRARY = MYLIBRARY || (function(){
    var _args = {}; // private

    return {
        init : function(Args) {
            _args = Args;
            // some other initialising
        },
        helloWorld : function(i) {
            return _args[i];
        }
    };
}());

script.js

// Here you can use the values defined in the html as if it were a global variable
var a = "Hello World " + MYLIBRARY.helloWorld(2);

alert(a);

これがplnkrです。それが役に立てば幸い!


5

グローバルスコープでは、意味上の違いはありません。

ただしa=0、値を宣言されていない変数に設定するため、実際には避けてください。

また、クロージャを使用して、グローバルスコープをまったく編集しないようにします。

(function() {
   // do stuff locally

   // Hoist something to global scope
   window.someGlobal = someLocal
}());

常にクロージャを使用し、絶対に必要な場合は常にグローバルスコープに引き上げます。とにかく、ほとんどの通信で非同期イベント処理を使用する必要があります。

@AvianMoncellorが言及したようにvar a = foo、ファイルスコープのグローバルを宣言するだけのIEバグがあります。これは、IEの悪名高い壊れたインタープリターの問題です。このバグはおなじみのように聞こえるので、おそらく本当です。

だから固執する window.globalName = someLocalpointer


2
「グローバルスコープでは、意味上の違いはありません。」実際には、巨大なセマンティック違いがあります、プロパティが定義されますするメカニズムは完全に異なっている-しかし、現実的にそれがわずかに沸く実際の(あなたができないことの違い)。deletevar
TJクラウダー、2011

@TJ Crowder知らなかった。変数宣言は変数オブジェクトのプロパティを設定していると思いました。削除できないことを知りませんでした。
レイノス

うん。を使用する場合は、以前に定義されていますvar。それらはまったく同じメカニズムであり、実際に同じ結果をもたらします。:-)
TJクラウダー、2011

@TJ Crowder varスコープの停止にジャンプすることを言及するのを忘れていました。
レイノス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.