[JavaScript] 借金返済と学ぶ、クロージャを利用したプライベート変数

前回の記事では、レキシカル環境とクロージャについてカイージとともに学んでいきました。後編となるこちらでは、クロージャを利用したプライベート変数(みたいなやつ)と、アロー関数とfunctionでのthisの挙動について書いていこうと思います。

⇩⇩前回の記事はこちらです⇩⇩

https://qiita.com/shima14142/items/35d47c65799251e73fd9

今回も、以下の登場人物が出現します。

## 登場人物
![スクリーンショット 2024-07-10 21.38.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/ad8d2843-fb40-7734-59fb-bfbd9af5dd41.png)
なんか二人増えてますね。~~原作ファンブチギレのメンバーたちです。~~

## 前回のお話も踏まえながら
1発4000円もするパチンコ"ヌーマ"に挑戦した二人。すると、そこには新たな敵、イチジョーと~~自分たちを裏切ったイシーダのおっちゃんの娘である~~イシーダヒロミがいるではありませんか。


![スクリーンショット 2024-07-10 23.24.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/78af2de9-c098-2e3f-b3a7-548a51ebf69f.png)

そのパチンコ台の仕組みの一部は以下のようになっていましたね。(少し中身が変わっています。)
~~~js
function Pachinko(money) {
  let Kaiji_money = money;
  return function (amount) {
    function (amount) {
      if (Kaiji_money - amount * 4000 <= 0) {
        Kaiji_money = 0;
        console.log("お金がつきました。補充してください。");
      } else {
        Kaiji_money -= amount * 4000;
        console.log(
          amount * 4000 + "円使用しました。残りのお金は: " + Kaiji_money + "円"
        );
      }
    );
  };
}

const UsedBall = Pachinko(1090000); //残金を投入
UsedBall(100); //400000円使用しました。残りのお金は: 690000円
~~~
ではこの台でどんどん打っていきましょう
~~~js
UsedBall(100);//[Log] 400000円使用しました。残りのお金は: 690000円 
UsedBall(100);//[Log] 400000円使用しました。残りのお金は: 290000円
UsedBall(100);//[Log] お金がつきました。補充してください。 
~~~

おや、お金がなくなってしまいました、、カイージくんピンチです！

## カイージの逆襲

そんな時「かイージくん！！」と聞き覚えのある声が、、
![スクリーンショット 2024-07-10 23.27.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/8017a3eb-7b74-bf56-6bc9-6ada65cf8f36.png)

なんと！(~~テーアイのブラックカードを使って~~)1000万円を持ったイシーダヒロミが駆けつけてくれました！

しかし、この関数、お金自体を`kaiji_money`に加えることができませんね。そこで、戻り値をオブジェクトにして、その中に`addMoney`という関数と`useMoney`の二つ作ってあげればいいんです！

~~~js
function Pachinko(money) {
  let Kaiji_money = money;
  return {
    usedBall: function (amount) {
      if (Kaiji_money - amount * 4000 <= 0) {
        Kaiji_money = 0;
        console.log("お金がつきました。補充してください。");
      } else {
        Kaiji_money -= amount * 4000;
        console.log(
          amount * 4000 + "円使用しました。残りのお金は: " + Kaiji_money + "円"
        );
      }
    },
    addMoney: function (amount) {
      Kaiji_money += amount;
      console.log(
        amount + "円追加されました。現在のお金は: " + Kaiji_money + "円"
      );
    },
  };
}

const pachinkoMachine = Pachinko(1090000); //残金を投入
~~~

使う時は、以下のようにメソッドとして使います。

~~~js
pachinkoMachine.usedBall(100); //[Log] 400000円使用しました。残りのお金は: 690000円
pachinkoMachine.usedBall(100); //[Log] 400000円使用しました。残りのお金は: 290000円
pachinkoMachine.usedBall(100); //[Log] お金がつきました。補充してください。

pachinkoMachine.addMoney(10000000);//10000000円追加されました。現在のお金は: 10000000円
~~~

お！10000000円追加され、現在のお金は: 10000000円となっているので、うまくできていますね。

しかし、クロージャは関数に保存すると言っていたのに、どうして`addMoney`は`usedBall`にアクセスできたのでしょうか。

以下のレキシカル環境をみていきましょう。
![スクリーンショット 2024-07-13 18.58.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/b919dd7c-e393-26f0-21ba-cdb5b3360632.png)

なんじゃこりゃ。と思った方はこちらの前半をご覧ください。

https://qiita.com/shima14142/items/35d47c65799251e73fd9

前回でも言ったのですが、レキシカル環境の「外側のレキシカル環境」に関数オブジェクトの「スコープ」を設定するので、クロージャとして保存しているのは`Pachinko`の方でしたよね。

だから、`addMoney`で`kaiji_money`がもしなかったら`Pachinko`で`kaiji_money`を探すんですね。

前回のお話から、戻り値がオブジェクトになっただけでした。

## なぜかトネーガワも参戦
今、カイージくんが頑張ってパチンコを回していますね。

するとここでトネーガワが急に話しかけてきました。

![スクリーンショット 2024-07-11 1.14.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/adb0d3ff-d734-713f-f566-da9eaf9f7552.png)


今回は、`PachinkoMachine`を、`Pachinko`のクロージャとして設定しましたが、以下のように`PachinkoMachine2`としてクロージャを新しく作って、それぞれ打ってみるとどのようになるのでしょうか。

~~~js
const pachinkoMachine = Pachinko(1090000); //109万円投入
const pachinkoMachine2 = Pachinko(1000000); //100万円投入

pachinkoMachine.usedBall(100);//400000円使用しました。残りのお金は: 690000円
pachinkoMachine.addMoney(1000000);//1000000円追加されました。現在のお金は: 1690000円

pachinkoMachine2.usedBall(100);//400000円使用しました。残りのお金は: 600000円
pachinkoMachine2.addMoney(1000000);//1000000円追加されました。現在のお金は: 1600000円
~~~

お、なぜかそれぞれ保存されています。`Pachinko`関数は一つだけなのに、、

ではレキシカル環境を見ていきましょう。
![スクリーンショット 2024-07-13 18.56.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/b4af011b-5788-51a9-6e06-4354cce0f6c8.png)


なかなかにえげつないですね。~~もう二度と作りたくはありません。~~

そうです、これをみたらわかる通り、新しくレキシカル環境を作り、別のクロージャとして保存しているのです！

なので、後からアクセスしたとしてもそれぞれの変数はクロージャごとに保存されているのです！！

(今回はわかりやすいように`Pachinko2`としていますが、`Pachinko(インスタンスは100万)`のような表記の方が相応しいと思われます。)

また、何を動作をしていない時でもみれるような関数`getMoney`も作っておきましょう。後で役に立ちます。

~~~js
function Pachinko(money) {
  let Kaiji_money = money;
  return {
    usedBall: function (amount) {
      if (Kaiji_money - amount * 4000 <= 0) {
        Kaiji_money = 0;
        console.log("お金がつきました。補充してください。");
      } else {
        Kaiji_money -= amount * 4000;
        console.log(
          amount * 4000 + "円使用しました。残りのお金は: " + Kaiji_money + "円"
        );
      }
    },
    addMoney: function (amount) {
      Kaiji_money += amount;
      console.log(
        amount + "円追加されました。現在のお金は: " + Kaiji_money + "円"
      );
    },
    getMoney: function () {
      console.log("現在のお金は: " + Kaiji_money + "円");
      return Kaiji_money;
    },
  };
}

const pachinkoMachine = Pachinko(1090000); //残金を投入
pachinkoMachine.getMoney();//現在のお金は: 1090000円
~~~
うまく動作していますね。

そして、、なんとカイージらはパチンコに勝ち、それぞれ2億円ずつ持ち帰ることにしました。

## Eカード：トネーガワとの最終決戦
2億を手にしたカイージ、そんな時、トネーガワからその2億を賭けないかと話を持ちかけられました。もちろんEカードです。

多分原作を読んだことない方々はここまで来てもう飽きてギブアップしてると思いますが、一応Eカードについて解説しておきます。

>皇帝側（インペラーサイド）と奴隷側（スレイブサイド）の2つの立場に分かれてプレイします。各プレイヤーは、それぞれ異なる種類のカードを持っています。

![スクリーンショット 2024-07-11 3.15.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/ff6720ec-25f3-563a-127f-09e5d0defb96.png)

>カードの選択と配置：各プレイヤーは、自分のデッキから1枚のカードを選び、同時に裏向きで場に出します。

![スクリーンショット 2024-07-11 3.18.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/2e0fadb7-62db-a076-fcc6-a589f5f91e8e.png)

>両プレイヤーがカードを裏返して公開し、勝敗を決定します。
その時の勝敗は以下のように決まります。
皇帝 vs 市民：**皇帝の勝ち**　    市民 vs 奴隷：**市民の勝ち**
奴隷 vs 皇帝：**奴隷の勝ち**　    市民 vs 市民：**引き分け**


![スクリーンショット 2024-07-11 3.25.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/f0d64568-cdd4-6539-e7c2-a89ccfaa5c40.png)


この勝負が決まるまで繰り返し続けます。

やってみないとわからないので、まずは試してみましょう。

Resultを押して、0.5x(倍率)にすると見やすいです。

<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="OJePzxG" data-pen-title="Untitled" data-user="shimaf4979" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shimaf4979/pen/OJePzxG">
  Untitled</a> by shimaf4979 (<a href="https://codepen.io/shimaf4979">@shimaf4979</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

 これのJavaScriptはどうなっているのでしょうか。

 見てみましょう。（今回は、手札の保持とどちらが勝利するかのコードの説明のみで、DOM等には触れません。あらかじめご了承ください。）
 ~~~js:createEcardGame.js
function createECardGame() {
  let player1Deck = { emperor: 1, citizen: 4 };
  let player2Deck = { slave: 1, citizen: 4 };

  return {
    drawCard: function (player, card) {
      if (player === 1) {
        if (card === "emperor" && player1Deck.emperor > 0) {
          player1Deck.emperor--;
          return "emperor";
        } else if (card === "citizen" && player1Deck.citizen > 0) {
          player1Deck.citizen--;
          return "citizen";
        }
      } else if (player === 2) {
        if (card === "slave" && player2Deck.slave > 0) {
          player2Deck.slave--;
          return "slave";
        } else if (card === "citizen" && player2Deck.citizen > 0) {
          player2Deck.citizen--;
          return "citizen";
        }
      }
      return null;
    },
    playRound: function (player1Card, player2Card) {
      if (player1Card === "emperor" && player2Card === "slave") {
        return "Player 2 wins";
      } else if (player1Card === "slave" && player2Card === "emperor") {
        return "Player 1 wins";
      } else if (player1Card === "emperor" && player2Card === "citizen") {
        return "Player 1 wins";
      } else if (player1Card === "citizen" && player2Card === "slave") {
        return "Player 2 wins";
      } else if (player1Card === player2Card) {
        return "Draw";
      } else {
        return "error";
      }
    },
    getCardCount: function () {
      return {
        player1: { ...player1Deck },
        player2: { ...player2Deck },
      };
    },
  };
}
 ~~~
このゲームの始め方は以下のような感じです。
 ~~~js
 // Eカードゲームのインスタンスを作成
const eCardGame = createECardGame();

// カードを引く
const player1Card1 = eCardGame.drawCard(1, "citizen");
const player2Card1 = eCardGame.drawCard(2, "citizen");

// ラウンドをプレイ
const result1 = eCardGame.playRound(player1Card1, player2Card1);
console.log(result1); //Draw

// カードの残り枚数を確認
console.log(eCardGame.getCardCount()); 
//{player1: {emperor: 1, citizen: 3}, player2: {slave: 1, citizen: 3}}

const player1Card2 = eCardGame.drawCard(1, "emperor");
const player2Card2 = eCardGame.drawCard(2, "slave");

const result2 = eCardGame.playRound(player1Card2, player2Card2);
console.log(result2); // 'Player 2 wins'

// カードの残り枚数を確認
console.log(eCardGame.getCardCount());
 //{player1: {emperor: 0, citizen: 3}, player2: {slave: 0, citizen: 3}}
 ~~~

 図でも書いておきます。

![スクリーンショット 2024-07-11 15.06.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/5f7f00b5-b50a-e959-e9ac-ed5be7277615.png)

今回は前回と違い、引数が二つあることでわかりずらいように見えますが、やっていることは同じですね。(anonymousではないのは処理中だからということで見逃してください。)

![スクリーンショット 2024-07-13 19.04.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/f93a68c8-6fe0-5594-a349-4cdd8790341e.png)



ここで、トネーガワがズルを仕掛けてきました。

![スクリーンショット 2024-07-11 16.58.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/6e52b6f2-eb1b-785a-b452-826947a71474.png)

なんと卑怯な、これでは負けかねません。
~~~js
eCardGame.player1Deck.emperor = 0;
~~~

すると、以下のような結果になりました。

~~~js
ypeError: undefined is not an object (evaluating 'eCardGame.player1Deck.emperor = 0')
~~~

どうしてでしょうか。

上で、`player1Deck`と`player2Deck`というデータは、createECardGame 関数のスコープ内に閉じ込められています。これらのデッキは関数外から直接アクセスすることはできないのです！

また、`getCardCount`、`drawCard`、`playRound`という内部関数も同様に関数のスコープ内に閉じ込められており、外部から直接呼び出すことはできません。

しかし、内容を知りたい時もありますよね、そういう時のために`getCardCount`があるのです。

こうして、データとそれに関連する操作を一つの単位（オブジェクト）にまとめ、外部からの不正なアクセスを防ぐことを、**カプセル化**といい、その時に使われる外部からの参照を禁止する変数を**プライベート変数といいます。**


![スクリーンショット 2024-07-13 19.03.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/2885b9f6-aab1-51b2-4c17-954672b83467.png)

## クロージャの中にクロージャ(ネストクロージャ)



今回は以上となります。やっと次回、thisの挙動についてかけそうです。

ここまでどうしてレキシカル環境について説明してきたか、その理由がわかると思います。

https://qiita.com/shima14142/items/9735b819b275a183dfca