変数のスコープについては、CやJavaなどの言語では「ブロックスコープ」という方式が採用されています。これとは異なり、JavaScript は「関数スコープ」アプローチを採用しています。変数のスコープは、変数が配置されている関数によってのみ決定され、if や for などの論理ブロックとは関係ありません。たとえば、次の例は、C、Java、その他の言語とは異なる JavaScript の動作を示しています。
C や Java などの「ブロック スコープ」言語では、if 文や for 文などの論理ブロックが終了すると、その論理ブロック内で定義された変数が破棄されます。 JavaScript は異なります。変数が関数内で定義されている限り、関数全体内のすべてのコードは、変数が定義される前であっても、その変数にアクセスできます。
上記の例では、in 関数が定義されていない場合、console.log(a) は ReferenceError をスローします。 a が関数内で定義されている場合、その定義が a 変数呼び出しステートメントの後にある場合でも、 a への呼び出しは正当な操作です (変数の定義が呼び出しステートメントの後に発生する場合、その変数の値は、呼び出しステートメントは未定義です)。実際、関数内で var キーワードを使用して定義されたすべての変数について、定義操作は関数の先頭に移動されます (代入操作は var が定義されている行に残ります)。これは JavaScript ではホイストと呼ばれます。たとえば、上記のコードは次と同等です:
JavaScript での変数の保存
に問い合わせると、JS での「関数スコープ」とホイスティングについてよく理解できます。変数はグローバルオブジェクトまたは関数呼び出しオブジェクトに格納されるため、関数内で変数を定義すると、関数内のどこに変数を定義しても、必然的に関数呼び出しオブジェクト内にこの値を持つ関数呼び出しオブジェクトが存在します。 . 変数と同じ名前のプロパティ。このようにして、関数内のどこからでも変数にアクセスできます。
関数呼び出しに関しては、JavaScript にはさらに興味深い概念があります。変数のスコープ チェーンです。変数はグローバル オブジェクトまたは関数呼び出しオブジェクトに格納されるため、変数にアクセスするときは複数の場所からアクセスできます。オブジェクトの値を取得します。次のコードを例として挙げます:
上記のコードのレベル 2 関数内で、x 変数にアクセスしようとすると、プログラムは 3 つのオブジェクトから対応する属性値を検索できます: レベル 2 関数の呼び出しに使用される関数呼び出しオブジェクト、関数呼び出しレベル 1 関数の呼び出しに使用されるオブジェクトとグローバル オブジェクト - 関数定義のネストされた関係に基づいて、JavaScript はグローバル オブジェクトと関数呼び出しオブジェクトで構成されるオブジェクト チェーンを生成します。変数にアクセスする場合、プログラムはアクセス ステートメントに最も近いオブジェクトから検索を開始します。検索が見つからない場合は、オブジェクト チェーンの上位レベルのオブジェクトからグローバル オブジェクトまで検索が続けられます。
このオブジェクトチェーンは変数のスコープに関係しているため、「スコープチェーン」とも呼ばれます。
スコープ チェーンを一時的に変更し、オブジェクトをスコープ チェーンの先頭に (最初にアクセスする関数オブジェクトとして) 挿入する必要がある場合は、with ステートメントを使用できます。
ただし、JavaScript の strict モードでは with ステートメントは無効になります。非 strict モードであっても with ステートメントの使用は推奨されません。