この記事では、スコープと、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);
}
スコープの種類
グローバル スコープ
グローバル スコープの変数には、プログラムのどこからでもアクセスできます。
file-1.js
と file-2.js
の 2 つの JavaScript ファイルをインポートする 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
の番号で増分します。もう 1 つの 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
値を保持していることがわかります。この問題は 2 番目の for
ループで修正されています。let
キーワードで宣言された j
変数のスコープは for
ループのブロックに設定されており、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 スコープは、ウェブ開発におけるより高度なコンセプトの 1 つです。そのため、このコンテンツをよくお読みになり、時間をかけて内容を理解できたことをうれしく思います。
スコープはユーザー向けの機能ではありません。影響があるのはコードを作成するウェブ デベロッパーのみですが、スコープの仕組みに関する知識は、バグが発生したときに修正するうえで役立ちます。