グローバル変数とローカル変数のスコープ

この記事では、スコープと、JavaScript でのスコープの仕組みについて説明します。

スコープは、変数にアクセスして使用するコンテキストを定義する、JavaScript やその他のプログラミング言語の基本的な概念です。JavaScript について学び、変数をさらに扱うことで、より便利でコードにも適用できるようになります。

スコープは次のことに役立ちます。

  • メモリをより効率的に使用: スコープにより、必要な場合にのみ変数を読み込むことができます。変数が範囲外である場合、現在実行中のコードで変数を利用できるようにする必要があります。
  • バグの検出と修正がより簡単に: 変数をローカル スコープで分離すると、コードのバグのトラブルシューティングが容易になります。これは、グローバル変数とは異なり、外部スコープのコードではローカル スコープの変数を操作できないという安心感があるためです。
  • 再利用可能なコードの小さなブロックを作成する: たとえば、外部スコープに依存しない純粋な関数を作成できます。このような関数は、最小限の変更で簡単に他の場所に移動できます。

対象範囲とは

変数のスコープにより、コード内で変数を使用できる場所が決まります。

JavaScript は、グローバル スコープまたはローカル スコープの変数を定義します。

  • グローバル スコープを持つ変数は、JavaScript コード内のすべてのスコープから使用できます。
  • ローカル スコープを持つ変数は、特定のローカル コンテキストでのみ使用でき、varletconst などのキーワードによって作成されます。varlet、または const キーワードを使用して関数内に変数を作成すると、その変数のスコープはローカルになります。

この記事の後半のセクションでは、ブロックスコープと字句スコープについて説明します。

  • ブロック スコープ変数は、ブロック ステートメントが定義されている中かっこの位置によって、ローカルで使用できます。ブロック スコープがあるのは、let キーワードまたは const キーワードで宣言された変数のみです。
  • 語彙スコープは、ソースコード内で変数が宣言されている場所に基づいて、その変数を使用可能な場所を特定します。クロージャを使用すると、字句環境と呼ばれる外部スコープで参照される変数へのアクセス権を、閉じられた関数に付与できます。

変数がスコープ内でアクセスされると、JavaScript は割り当てられた値を返すか、エラーを生成します。

変数を宣言するには:

  • varconst、または 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.jsfile-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する必要があります。

ローカル スコープと関数スコープ

varlet、または 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";
}

ブロック スコープ変数は、ifforwhile の各ステートメントで使用できます。

このコード スニペット内の 2 つの for ループに注意してください。1 つの for ループで、var キーワードを使用してイニシャライザ変数を宣言しています。この変数は、012 の番号で増分します。もう 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 つです。そのため、このコンテンツをよくお読みになり、時間をかけて内容を理解できたことをうれしく思います。

スコープはユーザー向けの機能ではありません。影響があるのはコードを作成するウェブ デベロッパーのみですが、スコープの仕組みに関する知識は、バグが発生したときに修正するうえで役立ちます。