この記事では、スコープと、JavaScript でのスコープの仕組みについて説明します。
スコープは、変数がアクセスおよび使用されるコンテキストを定義する、JavaScript やその他のプログラミング言語の基本的な概念です。JavaScript について学び、変数の操作を続けるうちに、コードがより有用になり、適用範囲が広がります。
スコープは以下に役立ちます。
- メモリをより効率的に使用する: スコープにより、必要な場合にのみ変数を読み込むことができます。変数がスコープ外の場合、現在実行中のコードで使用できるようにする必要はありません。
- バグの検出と修正が容易になる: 変数をローカル スコープで分離すると、コード内のバグのトラブルシューティングが容易になります。グローバル変数とは異なり、外部スコープからのコードはローカル スコープの変数を操作できないと信頼できるからです。
- 再利用可能なコードの小さなブロックを作成する: たとえば、外部スコープに依存しない純粋な関数を作成できます。このような関数は、最小限の変更で簡単に別の場所に移動できます。
対象範囲とは
変数のスコープにより、コード内のどこで変数を使用できるかが決まります。
JavaScript はグローバル スコープまたはローカル スコープの変数を定義します。
- グローバル スコープを持つ変数は、JavaScript コード内の他のすべてのスコープから使用できます。
- ローカル スコープを持つ変数は、特定のローカル コンテキスト内でのみ使用できます。また、
var
、let
、const
などのキーワードによって作成されます。var
、let
、またはconst
キーワードを使用して関数内で変数を作成する場合、その変数にはローカル スコープが設定されます。
この記事の後半のセクションでは、ブロックと語彙のスコープについて説明します。
- ブロック スコープ変数は、ブロック ステートメントが定義されている中かっこの場所によって、ブロックでローカルに利用できます。ブロック スコープを持つのは、
let
キーワードまたはconst
キーワードで宣言された変数のみです。 - 語彙スコープは、ソースコード内で変数が宣言されている場所を使用して、その変数が使用可能な場所を決定します。クロージャを使用して、囲まれた関数が、語彙環境と呼ばれる外側のスコープで参照される変数にアクセスできるようにします。
変数がそのスコープ内でアクセスされると、JavaScript は割り当てられた値を返すか、それ以外の場合はエラーを生成します。
変数を宣言するには:
- ローカル変数またはグローバル スコープ変数を宣言するには、
var
、const
、またはlet
キーワードを使用します。 - ブロック スコープ変数を宣言するには、
const
キーワードまたはlet
キーワードを使用します。
関数内で var
変数を宣言すると、その宣言により、その変数は最も近い関数で使用可能になります。var
キーワードを使用してブロック スコープを持つ変数を宣言することはできません。
スコープの例
この例では、greeting
変数が関数またはブロックの外部で宣言され、その値が現在のドキュメント内のすべてのコードで利用可能になるため、グローバル スコープを示しています。
const greeting = 'hello';
console.log(greeting); // 'hello'
グローバル スコープの例では、greeting
変数に hello
値が割り当てられます。
この例では、関数内で let
キーワードを使用して greeting
変数を宣言しているため、ローカル スコープを示しています。greeting
変数はローカル スコープの変数であり、関数外では使用できません。
function greet() {
let greeting = 'Hello World!';
console.log(greeting);
}
この例では、ブロック内で greeting
変数を宣言し、中かっこ内でのみ変数にアクセスできるようにブロック スコープを示しています。
if (true) {
const greeting = 'hello';
}
console.log(greeting); // ReferenceError: greeting is not defined
console.log
関数が greeting
変数の値を出力しようとすると、JavaScript は想定される hello
メッセージではなく、ReferenceError
エラー メッセージを返します。その理由は、
greeting
変数にブロック スコープがあり、最も近いブロックが if
条件文の一部であるため、エラーが返されます。ブロック内で宣言する let
変数と const
変数に、ブロックの外部からアクセスすることはできません。したがって、ブロック スコープを指定する中かっこ内の greeting
変数にのみアクセスできます。
この例では、console.log(message)
メソッドを中かっこ内で移動しているため、エラーを修正しています。更新したコードは、console.log(message)
メソッドをブロック内で再配置します。
if (true) {
const greeting = 'hello';
console.log(greeting);
}
スコープのタイプ
グローバル スコープ
プログラムのどこからでも、グローバル スコープを持つ変数にアクセスできます。
2 つの JavaScript ファイル(file-1.js
と file-2.js
)をインポートする HTML ファイルについて考えてみましょう。
<script src="file-1.js"></script>
<script src="file-2.js"></script>
この例では、globalMessage
変数にグローバル スコープがあり、関数の外部で書き込まれています。実行時および実行中は、JavaScript プログラムのどこからでも globalMessage
変数の値にアクセスできます。
file-1.js
ファイルと file-2.js
ファイルの内容は、次のコード スニペットで確認できます。どちらのファイルでも globalMessage
変数を使用できることに注意してください。
// file-1.js
function hello() {
var localMessage = 'Hello!';
}
var globalMessage = 'Hey there!';
// file-2.js
console.log(localMessage); // localMessage is not defined
console.log(globalMessage); // Hey there!
また、この記事では詳しく説明していない別のタイプのスコープもあります。JavaScript モジュール内の関数またはブロックの外で変数を作成した場合、その変数にはグローバル スコープではなくモジュール スコープが設定されます。モジュール スコープを持つ変数は、現在のモジュール内の任意の場所で使用できますが、他のファイルやモジュールからは使用できません。モジュール スコープの変数を他のファイルで使用できるようにするには、作成されたモジュールから変数をエクスポートし、変数にアクセスする必要があるモジュールからimportする必要があります。
ローカル スコープと関数スコープ
var
、let
、または const
キーワードを使用して JavaScript 関数で変数を作成すると、その変数は関数に対してローカルであるため、関数内からのみアクセスできます。ローカル変数は関数の開始時に作成され、関数の実行が終了すると実質的に削除されます。
この例では、addNumbers()
関数内で total
変数を宣言しています。addNumbers()
関数内では、a
、b,
、total
変数にのみアクセスできます。
function addNumbers(a, b) {
const total = a + b;
}
addNumbers(3, 4);
let
キーワードと const
キーワードを使用して変数に名前を付けることができます。let
キーワードを使用すると、JavaScript で変数を更新できます。ただし、const
キーワードを使用すると、変数は定数のままになります。
var variable1 = 'Declared with var';
var variable1 = 'Redeclared with var';
variable1; // Redeclared with var
let variable2 = 'Declared with let. Cannot be redeclared.';
variable2 = 'let cannot be redeclared, but can be updated';
variable2; // let cannot be redeclared, but can be updated
const variable3 = 'Declared with const. Cannot be redeclared or updated';
variable3; // Declared with const. Cannot be redeclared or updated
ブロック スコープ
ブロックは、1 つのステートメントまたは一連のステートメントをグループ化するために使用します。ブロック スコープのローカル変数を宣言するには、const
キーワードまたは let
キーワードを使用します。var
キーワードを使用してブロック スコープを持つ変数を宣言することはできません。
たとえば、このブロックでは、name
変数とその "Elizabeth"
値のスコープが中かっこ内に含まれています。ブロック スコープ内の変数は、ブロックの外部では使用できません。
{
const name = "Elizabeth";
}
ブロック スコープの変数は、if
、for
、while
ステートメントで使用できます。
このコード スニペットには 2 つの for
ループがあります。1 つの for
ループで、var
キーワードを使用してイニシャライザ変数を宣言します。イニシャライザ変数は、0
、1
、2
という数値をインクリメントします。他の for
ループでは、let
キーワードを使用してイニシャライザ変数を宣言します。
for (var i = 0; i < 2; i++) {
// ...
}
console.log(i); // 2
for (let j = 0; j < 2; j++) {
// ...
}
console.log(j); // The j variable isn't defined.
上記のコード例では、最初の for
ループの i
変数が for
ループの外部にリークし、var
キーワードでブロック スコープを使用していないため、2
値が保持されています。この問題は、let
キーワードで宣言された j
変数のスコープが for
ループのブロックに設定され、for
ループが終了した後も存在しない 2 番目の for
ループで修正されます。
別のスコープで変数名を再利用する
スコープを使用すると、同じ変数名を別のスコープの別の場所で再利用する場合でも、関数内の変数を分離できます。
次の例は、スコープを使用することで、同じ変数名を異なる関数で再利用する方法を示しています。
function listOne() {
let listItems = 10;
console.log(listItems); // 10
}
function listTwo() {
let listItems = 20;
console.log(listItems); // 20
}
listOne();
listTwo();
listOne()
関数と listTwo()
関数の listItems
変数には想定値が割り当てられているため、互いに競合しないでください。
クロージャと語彙のスコープ
クロージャとは、内側の関数が外側の関数スコープにアクセスできる密閉された関数のことです。このスコープは語彙環境とも呼ばれます。そのため、JavaScript ではクロージャを使用して、関数が外部の語彙環境を参照できるようにします。これにより、関数内のコードが、関数の外部で宣言された変数を参照できるようになります。実際、外部の語彙環境への参照チェーンをコーディングして、ある関数が関数から呼び出され、その関数が別の関数から呼び出されるようにすることもできます。
この例では、outer()
関数が呼び出されたときに作成される字句環境でクロージャを形成し、hello
変数をクローズしています。したがって、hello
変数は setTimeout
コールバック関数内で使用されます。
function outer() {
const hello = 'world';
setTimeout(function () {
console.log('Within the closure!', hello)
}, 100);
}
outer();
語彙スコープを使用すると、スコープは実行時ではなくソースコードのコンパイル時に決定されます。語彙環境の詳細については、語彙のスコープとクロージャをご覧ください。
モジュール
JavaScript モジュールは、JavaScript コードの整理に役立ちます。これらを適切に使用することで、コードベースが効果的に構造化され、コードの再利用が容易になります。JavaScript モジュールには、異なるファイル間で変数を共有するためにグローバル変数を使用するのではなく、変数のエクスポートとimportを行う手法が用意されています。
// hello.js file
function hello() {
return 'Hello world!';
}
export { hello };
// app.js file
import { hello } from './hello.js';
console.log(hello()); // Hello world!
スコープ ビジュアライザのデモ
スコープは、すべての JavaScript デベロッパーが理解しておくべき基本概念です。スコープ システムについて理解を深めるには、JS Scope Visualizer で独自のコードを記述してみてください。このデモでは、JavaScript スコープを視覚化できるようにコードの色を使用しています。
まとめ
この記事では、さまざまな種類のスコープについて説明します。JavaScript スコープは、ウェブ開発におけるより高度なコンセプトの一つです。ここまでに目を通し、このトピックを理解できたと思います。
スコープはユーザー向けの機能ではありません。影響を受けるのはコードを作成するウェブ デベロッパーだけですが、スコープの仕組みに関する知識があると、バグが発生したときに修正できます。