1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-10-18

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

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

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

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

はじめに

対象読者

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

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

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

スコープとは?

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

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

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

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

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

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

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

例を見てみましょう。

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

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

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

クロージャとは?

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

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

クロージャの定義

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

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

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

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

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

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

クロージャの使いどころ

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

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

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

例を見てみましょう。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

グローバル変数の乱用

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

悪い例:

var count = 0;

function incrementCount() {
  count++;
}

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

良い例:

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

const increment = createCounter();

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

this キーワードの誤用

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

悪い例:

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

obj.getValue();

良い例:

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

obj.getValue();

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

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

obj.getValue();

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

まとめ

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

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

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

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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?