初心者がハマりがちなスコープとクロージャを10分で理解する


こんにちは、とまだです。

JavaScript を本格的に学び始めると、スコープとクロージャという言葉をよく耳にすることがあると思います。

今回は、JavaScript における「スコープ」と「クロージャ」について、基本的な概念から実践的な使い方まで解説します。

難しい用語はできるだけ避け、日常生活の例を使って直感的に理解できるよう心がけました。

## はじめに

### 対象読者

- JavaScript を使い始めた初心者の方
- スコープとクロージャに苦手意識がある方
- 基本を押さえてスキルアップしたい方

### この記事を読んだ後に身につくスキル

- スコープの種類と違いを理解し、適切に使い分けられるようになる
- クロージャの仕組みを理解し、実践的に活用できるようになる
- よくある間違いを回避し、バグの少ないコードを書けるようになる

## スコープとは？

スコープとは、変数や関数の「有効範囲」のことです。
日本語で言えば「見える範囲」とか「使える範囲」というイメージです。

例えば、家の中を想像してみてください。
リビングにある本は家族全員が見ることができますが、自分の部屋にある日記は自分しか見ることができませんよね。

これと同じように、JavaScript でも変数や関数にはスコープがあります。

### グローバルスコープとローカルスコープの違い

JavaScript には「グローバルスコープ」と「ローカルスコープ」の 2 つのスコープがあります。

先ほどのリビングの例で言うと、次のようなイメージです。

1. グローバルスコープ：リビングのようなもの。どこからでもアクセスできるし、見える
2. ローカルスコープ：個人の部屋のようなもの。その部屋（関数）の中でしかアクセスできない

例を見てみましょう。

```javascript
// グローバルスコープ
const globalVar = '私は家族全員に見える';

function myRoom() {
  // ローカルスコープ
  const localVar = '私は自分の部屋でしか見えない';
  console.log(globalVar); // アクセス可能
  console.log(localVar); // アクセス可能
}

console.log(globalVar); // アクセス可能
console.log(localVar); // スコープ外なのでアクセス不可
```

## クロージャとは？

クロージャは、関数の中に定義された関数が、外側の関数のスコープにアクセスできる仕組みです。

ちょっと難しそうに聞こえますが、例を使って説明しましょう。

### クロージャの定義

クロージャは、「関数」と「その関数が作られた環境」のセットです。

もう少しわかりやすく言うと、関数が作られた時の状態（変数や関数）を覚えている関数のことです。

例えば、こんな感じです。

```javascript
function outerFunction(x) {
  return function innerFunction(y) {
    return x + y; // xは外側の関数の変数
  };
}

const add5 = outerFunction(5);
console.log(add5(3)); // 8
```

この例では、`innerFunction` が `outerFunction` の `x` にアクセスしています。
これがクロージャです。

### クロージャの使いどころ

パッと見ると、クロージャを使う場面が思いつかないかもしれませんが、実はとても便利な機能です。

たとえば、次のようなことができます。

1. プライベート変数の実現
2. 状態を持つ関数の作成
3. コールバック関数でのデータの保持

例を見てみましょう。

```javascript
function createCounter() {
  let count = 0;
  return function () {
    return ++count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
```

この例では、`count` 変数は外部からアクセスできません。

ですが、`createCounter` 関数が返す関数を通じて、間接的に操作しています。
言い換えると、`count` 変数は `counter` 関数の中に閉じ込められているということです。

中の関数が外側の状態を覚えているので、状態を持つ関数を作るのに便利です。

## 実践的なコード例で学ぶスコープとクロージャ

ここまでの知識を使って、実践的なコード例を見ていきましょう。

### カウンターを作ってみよう

先ほど少し触れましたが、クロージャを使ってカウンターを作ることができます。
これは、プライベートな状態を持つオブジェクトを簡単に作れる例です。

```javascript
function createCounter() {
  let count = 0;
  return {
    increment: function () {
      return ++count;
    },
    decrement: function () {
      return --count;
    },
    getCount: function () {
      return count;
    },
  };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getCount()); // 1
```

この例では、`count` 変数は外部からアクセスできませんが、返されたオブジェクトのメソッドを通じて操作できます。

こうすることで、外部から状態を変更される心配がなくなります。

また、`createCounter` 内の詳しい実装は隠蔽されているため、外部からは見えません。
言い換えると、カプセル化されたオブジェクトを作ることができます。

:::note info
カプセル化とは、オブジェクト指向プログラミングの基本的な概念の 1 つで、データとそれに関連する操作を 1 つの単位にまとめることです。
:::

### モジュールパターンを理解する

クロージャを使って、`モジュールパターン`というデザインパターンを実現することもできます。

これは、関連する機能をまとめて、公開したい部分だけを外部に公開する方法です。

```javascript
const calculator = (function () {
  let result = 0;

  function add(x) {
    result += x;
  }

  function subtract(x) {
    result -= x;
  }

  return {
    add: add,
    subtract: subtract,
    getResult: function () {
      return result;
    },
  };
})();

calculator.add(5);
calculator.subtract(2);
console.log(calculator.getResult()); // 3
```

この例では、`result` 変数は外部からアクセスできませんが、公開されたメソッドを通じて操作できます。
これにより、内部の実装を隠蔽しつつ、必要な機能だけを公開することができます。

## スコープとクロージャのよくある間違い

スコープとクロージャは強力な機能ですが、使い方を間違えるとバグの原因になることがあります。
ここでは、よくある間違いとその回避方法を紹介します。

※少し高度な内容になりますが、理解しておくとコードの品質が向上します。

### グローバル変数の乱用

グローバル変数は便利ですが、乱用するとコードの予測可能性が下がり、バグの原因になります。

悪い例：

```javascript
var count = 0;

function incrementCount() {
  count++;
}

// 他の場所でcountが変更される可能性がある
```

良い例：

```javascript
function createCounter() {
  let count = 0;
  return function () {
    return ++count;
  };
}

const increment = createCounter();
```

この良い例では、`count` 変数はクロージャの中に隠蔽されており、予期せぬ変更から守られています。

### this キーワードの誤用

`this` キーワードは、使用するコンテキストによって意味が変わるため、しばしば混乱の原因となります。

悪い例：

```javascript
const obj = {
  value: 42,
  getValue: function () {
    setTimeout(function () {
      console.log(this.value); // undefined
    }, 1000);
  },
};

obj.getValue();
```

良い例：

```javascript
const obj = {
  value: 42,
  getValue: function () {
    const self = this;
    setTimeout(function () {
      console.log(self.value); // 42
    }, 1000);
  },
};

obj.getValue();
```

この良い例では、`self` 変数を使って `this` の参照を保持しています。
あるいは、アロー関数を使うこともできます。

```javascript
const obj = {
  value: 42,
  getValue: function () {
    setTimeout(() => {
      console.log(this.value); // 42
    }, 1000);
  },
};

obj.getValue();
```

アロー関数は周囲のスコープの `this` を継承するため、このようなケースで特に便利です。

## まとめ

ここまでの内容をまとめると、次のようになります。

- スコープとは、変数や関数の有効範囲のこと
- グローバルスコープとローカルスコープは、どこからアクセスできるかの違い
- クロージャは、関数とその関数が作られた環境のセット
- クロージャを使うと、外側の状態を覚えた関数を作ることができる
- クロージャを使って、プライベート変数やモジュールパターンを実現できる

本格的に JavaScript を書き始めると、スコープやクロージャに関する知譵が必要になってきます。

今は完全に理解できなくても大丈夫ですが、もし困ったときにはこの記事を参考にしてみてください！