5

グローバル スコープと JavaScript 変数の問題と、それらの一般的な望ましくない点を理解しています。そして、あなたはそれらをどこにでも見つけることができます。以下は (ブラウザーで) 同等です。

var foo = 3; // foo === 3, window.foo === 3
bazz = 10; // bazz === 10, window.bazz === 10

グローバル スコープで var キーワードを使用して変数を宣言することは、コード内で var を使用せずに変数を宣言することと同じです。変数はルート (ウィンドウ) オブジェクトに割り当てられます。

私がよく目にする 1 つの手法 (Google アナリティクスの設定など) は次のとおりです。

var _gaq = _gaq || [];

...そして、_gaqが宣言されている場合はそれを使用し、そうでない場合は配列として作成するという推論に従います。これにより、グローバル変数 _gaq に既に割り当てられている値を不注意にコーディングしても上書きされなくなります。

私が理解していないのは、これがエラーをスローする理由です:

_gaq = _gaq || [];

それらは私と同等に見えます: _gaq は _gaq の値を取るか、配列として初期化する必要があります。しかし、参照エラーがスローされます-私の質問は、なぜそれらが異なるのですか?

4

4 に答える 4

5

宣言されていない変数を読み取ることはできません。それが_gaq || []、最後のケースの式で試みていることです。

この場合

 _gaq = _gaq || [];

_qaq以前に宣言されておらず、右側 ( _gaq || []) が評価されると、エラーがスローされます。

この場合に何が起こっているかを順を追って説明します。

代入演算子は、仕様のセクション 11.13.1で説明されています。

生産性AssignmentExpression : LeftHandSideExpression = AssignmentExpressionは次のように評価されます。

1.lrefを評価の結果とするLeftHandSideExpression
2.rrefを評価の結果とするAssignmentExpression
...

LeftHandSideExpression_gaqAssignmentExpression_gqa || []

そのため、変数が宣言されていないため、最初_qaqに評価され、解決できない参照が発生します。_gaqこの評価はエラーをスローしません。

その後_gqa || []、評価されます。これは であり、セクション 11.11で としてLogicalORExpression説明されています。この場合、は左側で、 はで、右側は です。 式は次のように評価されます。LogicalORExpression || LogicalANDExpressionLogicalORExpression_gaqLogicalANDExpression[]

1.lrefを評価の結果とするLogicalORExpression
2. ましょlvalGetValue(lref)
...

は宣言されていないlrefため、解決できない参照になることは既にわかっています。_gaqそれでは、何をしているのか見てみましょう(セクション 8.7.1GetValue定義されています。は に渡される値です):VGetValue

1. そうType(V)でない場合はReference、 を返しVます。
2.baseを呼び出した結果としGetBase(V)ます。
3. の場合IsUnresolvableReference(V)ReferenceError例外をスローします。
...

ご覧のとおりReferenceError、このプロシージャの 3 番目のステップでエラーがスローされ、代入の右辺を評価することによって実行されます。ここでエラーがスローされます。

では、なぜこれが起こらないのvar _gaq = _gaq || [];ですか?

この行:

var _gaq = _gaq || [];

実際には

var _gaq;
_gaq = _gaq || [];

巻き上げと呼ばれるもののため[MDN] . つまり、_gaqが評価されると、解決できない参照にはならず、値を持つ参照になります。undefined

(変数_gaqが既に宣言されている (そして潜在的に値を持っている) 場合、var _gaq効果はありません。)


_gaq関数内からグローバルに作成する場合は、次を参照して明示的windowに行います。

window._gaq = window._gaq || [];
于 2013-03-13T15:03:37.610 に答える
4

_gaqの右側が=以前にで宣言されていなかった場合var、参照エラーがスローされます。存在しない変数を参照しようとしています。「魔法」は、存在しない変数に割り当てるためだけに機能します。

言うようなものx = y + 1です; 問題は存在しないことではなく、存在しないことxですy

于 2013-03-13T14:55:54.643 に答える
1

現在の実行コンテキストのコンテキストチェーンに変数が見つからないため、これはエラーをスローします。解決できない変数にアクセスすると、エラーが発生します。

_gaq = _gaq || [];

一方、これは _gac を解決しようとし、window オブジェクトのメンバーとして検索しようとしますが、これはグローバル コンテキストの「ホルダー」オブジェクトであることが判明します。この場合の違いは、エラーはスローwindow._gaqされませんが、window オブジェクトでプロパティが見つからないため、 undefined が返されることです。

_gaq = window._gaq || [];

したがって、グローバル コンテキスト オブジェクトはウィンドウ (ブラウザーについて言えば) であるため、_gaq が定義されている場合、この 2 つのステートメントは同じ効果を持ちます。_gaq が定義されていない場合に違いがわかり、window オブジェクトを使用してアクセスすると、エラーが発生しないという利点があります。

于 2013-03-13T15:09:42.683 に答える
1

ここでの基本的な概念は巻き上げであり、実際にはしばしば注意が必要です。変数は関数のスコープの先頭で定義されますが、割り当ては定義されている場所で行われます。

これにより、割り当ての実際のコード行が実行される前にvar _gaq = _gaq、変数が実際に定義されます。これは、割り当てが発生したときに、変数が既にウィンドウ スコープにあることを意味します。_gaq の前に var がないと巻き上げが発生しないため、割り当てが実行されたときに _gaq がまだ存在せず、参照エラーが発生します。

これを実際に見たい場合は、_gaq 変数がいつウィンドウ オブジェクトに追加されるかを次のように確認できます。

function printIsPropOnWindow(propToCheck)
{
    for (prop in window)
    {
        if (prop == propToCheck)
        {
            console.warn('YES, prop ' + prop + ' was on the window object');
            return;
        }
    }
    console.warn('NO, prop ' + propToCheck + ' was NOT on the window object');
}


try {
    var _gaq = function() {
        printIsPropOnWindow("_gaq");
        return a;
    }();
} catch (ex) {
    printIsPropOnWindow("_gaq");
}
_gaq = "1";
printIsPropOnWindow("_gaq");

これをそのまま 1 回試し、_gaq を削除する前の var で 1 回試してみると、非常に異なる結果が表示されます。

于 2013-03-13T15:37:07.430 に答える